discordrb 3.5.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.devcontainer/Dockerfile +13 -0
- data/.devcontainer/devcontainer.json +29 -0
- data/.devcontainer/postcreate.sh +4 -0
- data/.github/workflows/ci.yml +78 -0
- data/.github/workflows/codeql.yml +3 -3
- data/.github/workflows/deploy.yml +54 -0
- data/.github/workflows/release.yml +45 -0
- data/.rubocop.yml +52 -2
- data/CHANGELOG.md +95 -0
- data/README.md +5 -5
- data/discordrb-webhooks.gemspec +1 -1
- data/discordrb.gemspec +16 -11
- data/lib/discordrb/api/application.rb +84 -8
- data/lib/discordrb/api/channel.rb +51 -13
- data/lib/discordrb/api/interaction.rb +15 -6
- data/lib/discordrb/api/invite.rb +1 -1
- data/lib/discordrb/api/server.rb +96 -60
- data/lib/discordrb/api/user.rb +12 -2
- data/lib/discordrb/api/webhook.rb +20 -5
- data/lib/discordrb/api.rb +16 -20
- data/lib/discordrb/bot.rb +139 -53
- data/lib/discordrb/cache.rb +15 -1
- data/lib/discordrb/commands/command_bot.rb +7 -17
- data/lib/discordrb/commands/parser.rb +7 -7
- data/lib/discordrb/container.rb +46 -0
- data/lib/discordrb/data/activity.rb +1 -1
- data/lib/discordrb/data/application.rb +1 -0
- data/lib/discordrb/data/attachment.rb +23 -3
- data/lib/discordrb/data/avatar_decoration.rb +26 -0
- data/lib/discordrb/data/call.rb +22 -0
- data/lib/discordrb/data/channel.rb +140 -15
- data/lib/discordrb/data/collectibles.rb +45 -0
- data/lib/discordrb/data/embed.rb +10 -3
- data/lib/discordrb/data/emoji.rb +20 -1
- data/lib/discordrb/data/integration.rb +3 -0
- data/lib/discordrb/data/interaction.rb +164 -27
- data/lib/discordrb/data/member.rb +145 -28
- data/lib/discordrb/data/message.rb +198 -51
- data/lib/discordrb/data/overwrite.rb +2 -0
- data/lib/discordrb/data/primary_server.rb +60 -0
- data/lib/discordrb/data/profile.rb +2 -7
- data/lib/discordrb/data/reaction.rb +2 -1
- data/lib/discordrb/data/recipient.rb +1 -1
- data/lib/discordrb/data/role.rb +151 -22
- data/lib/discordrb/data/server.rb +115 -41
- data/lib/discordrb/data/server_preview.rb +68 -0
- data/lib/discordrb/data/snapshot.rb +110 -0
- data/lib/discordrb/data/user.rb +68 -8
- data/lib/discordrb/data/voice_region.rb +1 -0
- data/lib/discordrb/data/webhook.rb +2 -5
- data/lib/discordrb/data.rb +6 -0
- data/lib/discordrb/errors.rb +5 -2
- data/lib/discordrb/events/await.rb +1 -1
- data/lib/discordrb/events/channels.rb +37 -0
- data/lib/discordrb/events/generic.rb +2 -0
- data/lib/discordrb/events/guilds.rb +6 -1
- data/lib/discordrb/events/interactions.rb +135 -42
- data/lib/discordrb/events/invites.rb +2 -0
- data/lib/discordrb/events/members.rb +19 -2
- data/lib/discordrb/events/message.rb +39 -8
- data/lib/discordrb/events/presence.rb +2 -0
- data/lib/discordrb/events/raw.rb +1 -0
- data/lib/discordrb/events/reactions.rb +2 -0
- data/lib/discordrb/events/roles.rb +2 -0
- data/lib/discordrb/events/threads.rb +10 -6
- data/lib/discordrb/events/typing.rb +1 -0
- data/lib/discordrb/events/voice_server_update.rb +1 -0
- data/lib/discordrb/events/voice_state_update.rb +1 -0
- data/lib/discordrb/events/webhooks.rb +1 -0
- data/lib/discordrb/gateway.rb +29 -13
- data/lib/discordrb/paginator.rb +3 -3
- data/lib/discordrb/permissions.rb +54 -43
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/websocket.rb +0 -10
- data/lib/discordrb.rb +17 -1
- metadata +53 -25
- data/.circleci/config.yml +0 -152
data/lib/discordrb/api.rb
CHANGED
|
@@ -203,6 +203,11 @@ module Discordrb::API
|
|
|
203
203
|
"#{cdn_url}/splashes/#{server_id}/#{splash_id}.#{format}"
|
|
204
204
|
end
|
|
205
205
|
|
|
206
|
+
# Make a discovery splash URL from server and splash IDs
|
|
207
|
+
def discovery_splash_url(server_id, splash_id, format = 'webp')
|
|
208
|
+
"#{cdn_url}/discovery-splashes/#{server_id}/#{splash_id}.#{format}"
|
|
209
|
+
end
|
|
210
|
+
|
|
206
211
|
# Make a banner URL from server and banner IDs
|
|
207
212
|
def banner_url(server_id, banner_id, format = 'webp')
|
|
208
213
|
"#{cdn_url}/banners/#{server_id}/#{banner_id}.#{format}"
|
|
@@ -231,28 +236,19 @@ module Discordrb::API
|
|
|
231
236
|
"#{cdn_url}/role-icons/#{role_id}/#{icon_hash}.#{format}"
|
|
232
237
|
end
|
|
233
238
|
|
|
234
|
-
#
|
|
235
|
-
def
|
|
236
|
-
|
|
237
|
-
:auth_login,
|
|
238
|
-
nil,
|
|
239
|
-
:post,
|
|
240
|
-
"#{api_base}/auth/login",
|
|
241
|
-
email: email,
|
|
242
|
-
password: password
|
|
243
|
-
)
|
|
239
|
+
# make an avatar decoration URL from an avatar decoration ID.
|
|
240
|
+
def avatar_decoration_url(avatar_decoration_id, format = 'png')
|
|
241
|
+
"#{cdn_url}/avatar-decoration-presets/#{avatar_decoration_id}.#{format}"
|
|
244
242
|
end
|
|
245
243
|
|
|
246
|
-
#
|
|
247
|
-
def
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
Authorization: token
|
|
255
|
-
)
|
|
244
|
+
# make a nameplate URL from the nameplate asset.
|
|
245
|
+
def nameplate_url(nameplate_asset, format = 'webm')
|
|
246
|
+
"#{cdn_url}/assets/collectibles/#{nameplate_asset.delete_suffix('/')}/asset.#{format}"
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# make a server tag badge URL from a server ID and badge ID.
|
|
250
|
+
def server_tag_badge_url(server_id, badge_id, format = 'webp')
|
|
251
|
+
"#{cdn_url}/guild-tag-badges/#{server_id}/#{badge_id}.#{format}"
|
|
256
252
|
end
|
|
257
253
|
|
|
258
254
|
# Create an OAuth application
|
data/lib/discordrb/bot.rb
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require 'rest-client'
|
|
4
4
|
require 'zlib'
|
|
5
|
-
require 'set'
|
|
6
5
|
|
|
7
6
|
require 'discordrb/events/message'
|
|
8
7
|
require 'discordrb/events/typing'
|
|
@@ -107,9 +106,10 @@ module Discordrb
|
|
|
107
106
|
# to Discord's gateway. `:none` will request that no payloads are received compressed (not recommended for
|
|
108
107
|
# production bots). `:large` will request that large payloads are received compressed. `:stream` will request
|
|
109
108
|
# that all data be received in a continuous compressed stream.
|
|
110
|
-
# @param intents [:all, :unprivileged, Array<Symbol>, :none] Gateway intents that this bot requires. `:all` will
|
|
109
|
+
# @param intents [:all, :unprivileged, Array<Symbol>, :none, Integer] Gateway intents that this bot requires. `:all` will
|
|
111
110
|
# request all intents. `:unprivileged` will request only intents that are not defined as "Privileged". `:none`
|
|
112
|
-
# will request no intents. An array of symbols will request only those intents specified.
|
|
111
|
+
# will request no intents. An array of symbols will request only those intents specified. An integer value will request
|
|
112
|
+
# exactly all the intents specified in the bitwise value.
|
|
113
113
|
# @see Discordrb::INTENTS
|
|
114
114
|
def initialize(
|
|
115
115
|
log_mode: :normal,
|
|
@@ -307,13 +307,21 @@ module Discordrb
|
|
|
307
307
|
# Creates an OAuth invite URL that can be used to invite this bot to a particular server.
|
|
308
308
|
# @param server [Server, nil] The server the bot should be invited to, or nil if a general invite should be created.
|
|
309
309
|
# @param permission_bits [String, Integer] Permission bits that should be appended to invite url.
|
|
310
|
+
# @param redirect_uri [String] Redirect URI that should be appended to invite url.
|
|
311
|
+
# @param scopes [Array<String>] Scopes that should be appended to invite url.
|
|
310
312
|
# @return [String] the OAuth invite URL.
|
|
311
|
-
def invite_url(server: nil, permission_bits: nil)
|
|
313
|
+
def invite_url(server: nil, permission_bits: nil, redirect_uri: nil, scopes: ['bot'])
|
|
312
314
|
@client_id ||= bot_application.id
|
|
313
315
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
316
|
+
query = URI.encode_www_form({
|
|
317
|
+
client_id: @client_id,
|
|
318
|
+
guild_id: server&.id,
|
|
319
|
+
permissions: permission_bits,
|
|
320
|
+
redirect_uri: redirect_uri,
|
|
321
|
+
scope: scopes.join(' ')
|
|
322
|
+
}.compact)
|
|
323
|
+
|
|
324
|
+
"https://discord.com/oauth2/authorize?#{query}"
|
|
317
325
|
end
|
|
318
326
|
|
|
319
327
|
# @return [Hash<Integer => VoiceBot>] the voice connections this bot currently has, by the server ID to which they are connected.
|
|
@@ -392,17 +400,20 @@ module Discordrb
|
|
|
392
400
|
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
|
|
393
401
|
# @param embeds [Hash, Discordrb::Webhooks::Embed, Array<Hash>, Array<Discordrb::Webhooks::Embed> nil] The rich embed(s) to append to this message.
|
|
394
402
|
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
|
|
395
|
-
# @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.
|
|
396
404
|
# @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), 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.
|
|
397
408
|
# @return [Message] The message that was sent.
|
|
398
|
-
def send_message(channel, content, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
|
|
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)
|
|
399
410
|
channel = channel.resolve_id
|
|
400
411
|
debug("Sending message to #{channel} with content '#{content}'")
|
|
401
412
|
allowed_mentions = { parse: [] } if allowed_mentions == false
|
|
402
|
-
message_reference = { message_id: message_reference.
|
|
413
|
+
message_reference = { message_id: message_reference.resolve_id } if message_reference.respond_to?(:resolve_id)
|
|
403
414
|
embeds = (embeds.instance_of?(Array) ? embeds.map(&:to_hash) : [embeds&.to_hash]).compact
|
|
404
415
|
|
|
405
|
-
response = API::Channel.create_message(token, channel, content, tts, embeds,
|
|
416
|
+
response = API::Channel.create_message(token, channel, content, tts, embeds, nonce, attachments, allowed_mentions&.to_hash, message_reference, components, flags, enforce_nonce)
|
|
406
417
|
Message.new(JSON.parse(response), self)
|
|
407
418
|
end
|
|
408
419
|
|
|
@@ -417,11 +428,14 @@ module Discordrb
|
|
|
417
428
|
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
|
|
418
429
|
# @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
|
|
419
430
|
# @param components [View, Array<Hash>] Interaction components to associate with this message.
|
|
420
|
-
|
|
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)
|
|
421
435
|
Thread.new do
|
|
422
436
|
Thread.current[:discordrb_name] = "#{@current_thread}-temp-msg"
|
|
423
437
|
|
|
424
|
-
message = send_message(channel, content, tts, embeds, attachments, allowed_mentions, message_reference, components)
|
|
438
|
+
message = send_message(channel, content, tts, embeds, attachments, allowed_mentions, message_reference, components, flags, nonce, enforce_nonce)
|
|
425
439
|
sleep(timeout)
|
|
426
440
|
message.delete
|
|
427
441
|
end
|
|
@@ -455,20 +469,6 @@ module Discordrb
|
|
|
455
469
|
Message.new(JSON.parse(response), self)
|
|
456
470
|
end
|
|
457
471
|
|
|
458
|
-
# Creates a server on Discord with a specified name and a region.
|
|
459
|
-
# @note Discord's API doesn't directly return the server when creating it, so this method
|
|
460
|
-
# waits until the data has been received via the websocket. This may make the execution take a while.
|
|
461
|
-
# @param name [String] The name the new server should have. Doesn't have to be alphanumeric.
|
|
462
|
-
# @param region [Symbol] The region where the server should be created, for example 'eu-central' or 'hongkong'.
|
|
463
|
-
# @return [Server] The server that was created.
|
|
464
|
-
def create_server(name, region = :'eu-central')
|
|
465
|
-
response = API::Server.create(token, name, region)
|
|
466
|
-
id = JSON.parse(response)['id'].to_i
|
|
467
|
-
sleep 0.1 until (server = @servers[id])
|
|
468
|
-
debug "Successfully created server #{server.id} with name #{server.name}"
|
|
469
|
-
server
|
|
470
|
-
end
|
|
471
|
-
|
|
472
472
|
# Creates a new application to do OAuth authorization with. This allows you to use OAuth to authorize users using
|
|
473
473
|
# Discord. For information how to use this, see the docs: https://discord.com/developers/docs/topics/oauth2
|
|
474
474
|
# @param name [String] What your application should be called.
|
|
@@ -517,7 +517,7 @@ module Discordrb
|
|
|
517
517
|
end
|
|
518
518
|
end
|
|
519
519
|
elsif /(?<animated>^a|^${0}):(?<name>\w+):(?<id>\d+)/ =~ mention
|
|
520
|
-
array_to_return << (emoji(id) || Emoji.new({ 'animated' =>
|
|
520
|
+
array_to_return << (emoji(id) || Emoji.new({ 'animated' => animated != '', 'name' => name, 'id' => id }, self, nil))
|
|
521
521
|
end
|
|
522
522
|
end
|
|
523
523
|
array_to_return
|
|
@@ -824,7 +824,7 @@ module Discordrb
|
|
|
824
824
|
# end
|
|
825
825
|
# end
|
|
826
826
|
# end
|
|
827
|
-
def register_application_command(name, description, server_id: nil, default_permission: nil, type: :chat_input)
|
|
827
|
+
def register_application_command(name, description, server_id: nil, default_permission: nil, type: :chat_input, default_member_permissions: nil, contexts: nil, nsfw: false)
|
|
828
828
|
type = ApplicationCommand::TYPES[type] || type
|
|
829
829
|
|
|
830
830
|
builder = Interactions::OptionBuilder.new
|
|
@@ -832,9 +832,9 @@ module Discordrb
|
|
|
832
832
|
yield(builder, permission_builder) if block_given?
|
|
833
833
|
|
|
834
834
|
resp = if server_id
|
|
835
|
-
API::Application.create_guild_command(@token, profile.id, server_id, name, description, builder.to_a, default_permission, type)
|
|
835
|
+
API::Application.create_guild_command(@token, profile.id, server_id, name, description, builder.to_a, default_permission, type, default_member_permissions, contexts, nsfw)
|
|
836
836
|
else
|
|
837
|
-
API::Application.create_global_command(@token, profile.id, name, description, builder.to_a, default_permission, type)
|
|
837
|
+
API::Application.create_global_command(@token, profile.id, name, description, builder.to_a, default_permission, type, default_member_permissions, contexts, nsfw)
|
|
838
838
|
end
|
|
839
839
|
cmd = ApplicationCommand.new(JSON.parse(resp), self, server_id)
|
|
840
840
|
|
|
@@ -849,7 +849,7 @@ module Discordrb
|
|
|
849
849
|
|
|
850
850
|
# @yieldparam [OptionBuilder]
|
|
851
851
|
# @yieldparam [PermissionBuilder]
|
|
852
|
-
def edit_application_command(command_id, server_id: nil, name: nil, description: nil, default_permission: nil, type: :chat_input)
|
|
852
|
+
def edit_application_command(command_id, server_id: nil, name: nil, description: nil, default_permission: nil, type: :chat_input, default_member_permissions: nil, contexts: nil, nsfw: nil)
|
|
853
853
|
type = ApplicationCommand::TYPES[type] || type
|
|
854
854
|
|
|
855
855
|
builder = Interactions::OptionBuilder.new
|
|
@@ -858,9 +858,9 @@ module Discordrb
|
|
|
858
858
|
yield(builder, permission_builder) if block_given?
|
|
859
859
|
|
|
860
860
|
resp = if server_id
|
|
861
|
-
API::Application.edit_guild_command(@token, profile.id, server_id, command_id, name, description, builder.to_a, default_permission, type)
|
|
861
|
+
API::Application.edit_guild_command(@token, profile.id, server_id, command_id, name, description, builder.to_a, default_permission, type, default_member_permissions, contexts, nsfw)
|
|
862
862
|
else
|
|
863
|
-
API::Application.
|
|
863
|
+
API::Application.edit_global_command(@token, profile.id, command_id, name, description, builder.to_a, default_permission, type, default_member_permissions, contexts, nsfw)
|
|
864
864
|
end
|
|
865
865
|
cmd = ApplicationCommand.new(JSON.parse(resp), self, server_id)
|
|
866
866
|
|
|
@@ -895,6 +895,46 @@ module Discordrb
|
|
|
895
895
|
API::Application.edit_guild_command_permissions(@token, profile.id, server_id, command_id, permissions)
|
|
896
896
|
end
|
|
897
897
|
|
|
898
|
+
# Fetches all the application emojis that the bot can use.
|
|
899
|
+
# @return [Array<Emoji>] Returns an array of emoji objects.
|
|
900
|
+
def application_emojis
|
|
901
|
+
response = API::Application.list_application_emojis(@token, profile.id)
|
|
902
|
+
JSON.parse(response)['items'].map { |emoji| Emoji.new(emoji, self) }
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
# Fetches a single application emoji from its ID.
|
|
906
|
+
# @param emoji_id [Integer, String] ID of the application emoji.
|
|
907
|
+
# @return [Emoji] The application emoji.
|
|
908
|
+
def application_emoji(emoji_id)
|
|
909
|
+
response = API::Application.get_application_emoji(@token, profile.id, emoji_id.resolve_id)
|
|
910
|
+
Emoji.new(JSON.parse(response), self)
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
# Creates a new custom emoji that can be used by this application.
|
|
914
|
+
# @param name [String] The name of emoji to create.
|
|
915
|
+
# @param image [String, #read] Base64 string with the image data, or an object that responds to #read.
|
|
916
|
+
# @return [Emoji] The emoji that has been created.
|
|
917
|
+
def create_application_emoji(name:, image:)
|
|
918
|
+
image = image.respond_to?(:read) ? Discordrb.encode64(image) : image
|
|
919
|
+
response = API::Application.create_application_emoji(@token, profile.id, name, image)
|
|
920
|
+
Emoji.new(JSON.parse(response), self)
|
|
921
|
+
end
|
|
922
|
+
|
|
923
|
+
# Edits an existing application emoji.
|
|
924
|
+
# @param emoji_id [Integer, String, Emoji] ID of the application emoji to edit.
|
|
925
|
+
# @param name [String] The new name of the emoji.
|
|
926
|
+
# @return [Emoji] Returns the updated emoji object on success.
|
|
927
|
+
def edit_application_emoji(emoji_id, name:)
|
|
928
|
+
response = API::Application.edit_application_emoji(@token, profile.id, emoji_id.resolve_id, name)
|
|
929
|
+
Emoji.new(JSON.parse(response), self)
|
|
930
|
+
end
|
|
931
|
+
|
|
932
|
+
# Deletes an existing application emoji.
|
|
933
|
+
# @param emoji_id [Integer, String, Emoji] ID of the application emoji to delete.
|
|
934
|
+
def delete_application_emoji(emoji_id)
|
|
935
|
+
API::Application.delete_application_emoji(@token, profile.id, emoji_id.resolve_id)
|
|
936
|
+
end
|
|
937
|
+
|
|
898
938
|
private
|
|
899
939
|
|
|
900
940
|
# Throws a useful exception if there's currently no gateway connection.
|
|
@@ -1091,12 +1131,12 @@ module Discordrb
|
|
|
1091
1131
|
server_id = data['guild_id'].to_i
|
|
1092
1132
|
server = self.server(server_id)
|
|
1093
1133
|
|
|
1094
|
-
|
|
1095
|
-
member.
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1134
|
+
# Only attempt to update members that're already cached
|
|
1135
|
+
if (member = server.member(data['user']['id'].to_i, false))
|
|
1136
|
+
member.update_data(data)
|
|
1137
|
+
else
|
|
1138
|
+
ensure_user(data['user'])
|
|
1139
|
+
end
|
|
1100
1140
|
end
|
|
1101
1141
|
|
|
1102
1142
|
# Internal handler for GUILD_MEMBER_DELETE
|
|
@@ -1212,7 +1252,7 @@ module Discordrb
|
|
|
1212
1252
|
|
|
1213
1253
|
def handle_dispatch(type, data)
|
|
1214
1254
|
# Check whether there are still unavailable servers and there have been more than 10 seconds since READY
|
|
1215
|
-
if @unavailable_servers&.positive? && (Time.now - @unavailable_timeout_time) > 10 && !(
|
|
1255
|
+
if @unavailable_servers&.positive? && (Time.now - @unavailable_timeout_time) > 10 && !(@intents || 0).nobits?(INTENTS[:servers])
|
|
1216
1256
|
# The server streaming timed out!
|
|
1217
1257
|
LOGGER.debug("Server streaming timed out with #{@unavailable_servers} servers remaining")
|
|
1218
1258
|
LOGGER.debug('Calling ready now because server loading is taking a long time. Servers may be unavailable due to an outage, or your bot is on very large servers.')
|
|
@@ -1232,6 +1272,8 @@ module Discordrb
|
|
|
1232
1272
|
|
|
1233
1273
|
@profile = Profile.new(data['user'], self)
|
|
1234
1274
|
|
|
1275
|
+
@client_id ||= data['application']['id']&.to_i
|
|
1276
|
+
|
|
1235
1277
|
# Initialize servers
|
|
1236
1278
|
@servers = {}
|
|
1237
1279
|
|
|
@@ -1274,6 +1316,8 @@ module Discordrb
|
|
|
1274
1316
|
id = data['guild_id'].to_i
|
|
1275
1317
|
server = server(id)
|
|
1276
1318
|
server.process_chunk(data['members'], data['chunk_index'], data['chunk_count'])
|
|
1319
|
+
when :USER_UPDATE
|
|
1320
|
+
@profile = Profile.new(data, self)
|
|
1277
1321
|
when :INVITE_CREATE
|
|
1278
1322
|
invite = Invite.new(data, self)
|
|
1279
1323
|
raise_event(InviteCreateEvent.new(data, invite, self))
|
|
@@ -1290,12 +1334,22 @@ module Discordrb
|
|
|
1290
1334
|
return
|
|
1291
1335
|
end
|
|
1292
1336
|
|
|
1337
|
+
if !should_parse_self && profile.id == data['author']['id'].to_i
|
|
1338
|
+
debug('Ignored message from the current bot')
|
|
1339
|
+
return
|
|
1340
|
+
end
|
|
1341
|
+
|
|
1293
1342
|
# If create_message is overwritten with a method that returns the parsed message, use that instead, so we don't
|
|
1294
1343
|
# parse the message twice (which is just thrown away performance)
|
|
1295
1344
|
message = create_message(data)
|
|
1296
1345
|
message = Message.new(data, self) unless message.is_a? Message
|
|
1297
1346
|
|
|
1298
|
-
|
|
1347
|
+
# Update the existing member if it exists in the cache.
|
|
1348
|
+
if data['member']
|
|
1349
|
+
member = message.channel.server&.member(data['author']['id'].to_i, false)
|
|
1350
|
+
data['member']['user'] = data['author']
|
|
1351
|
+
member&.update_data(data['member'])
|
|
1352
|
+
end
|
|
1299
1353
|
|
|
1300
1354
|
# Dispatch a ChannelCreateEvent for channels we don't have cached
|
|
1301
1355
|
if message.channel.private? && @pm_channels[message.channel.recipient.id].nil?
|
|
@@ -1319,18 +1373,28 @@ module Discordrb
|
|
|
1319
1373
|
when :MESSAGE_UPDATE
|
|
1320
1374
|
update_message(data)
|
|
1321
1375
|
|
|
1376
|
+
if !should_parse_self && profile.id == data['author']['id'].to_i
|
|
1377
|
+
debug('Ignored message from the current bot')
|
|
1378
|
+
return
|
|
1379
|
+
end
|
|
1380
|
+
|
|
1322
1381
|
message = Message.new(data, self)
|
|
1323
1382
|
|
|
1324
1383
|
event = MessageUpdateEvent.new(message, self)
|
|
1325
1384
|
raise_event(event)
|
|
1326
1385
|
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
unless message.author
|
|
1386
|
+
if data['author'].nil?
|
|
1330
1387
|
LOGGER.debug("Edited a message with nil author! Content: #{message.content.inspect}, channel: #{message.channel.inspect}")
|
|
1331
1388
|
return
|
|
1332
1389
|
end
|
|
1333
1390
|
|
|
1391
|
+
# Update the existing member if it exists in the cache.
|
|
1392
|
+
if data['member']
|
|
1393
|
+
member = message.channel.server&.member(data['author']['id'].to_i, false)
|
|
1394
|
+
data['member']['user'] = data['author']
|
|
1395
|
+
member&.update_data(data['member'])
|
|
1396
|
+
end
|
|
1397
|
+
|
|
1334
1398
|
event = MessageEditEvent.new(message, self)
|
|
1335
1399
|
raise_event(event)
|
|
1336
1400
|
when :MESSAGE_DELETE
|
|
@@ -1368,6 +1432,12 @@ module Discordrb
|
|
|
1368
1432
|
|
|
1369
1433
|
return if profile.id == data['user_id'].to_i && !should_parse_self
|
|
1370
1434
|
|
|
1435
|
+
if data['member']
|
|
1436
|
+
server = self.server(data['guild_id'].to_i)
|
|
1437
|
+
|
|
1438
|
+
server&.cache_member(Member.new(data['member'], server, self))
|
|
1439
|
+
end
|
|
1440
|
+
|
|
1371
1441
|
event = ReactionAddEvent.new(data, self)
|
|
1372
1442
|
raise_event(event)
|
|
1373
1443
|
when :MESSAGE_REACTION_REMOVE
|
|
@@ -1442,6 +1512,12 @@ module Discordrb
|
|
|
1442
1512
|
remove_recipient(data)
|
|
1443
1513
|
|
|
1444
1514
|
event = ChannelRecipientRemoveEvent.new(data, self)
|
|
1515
|
+
raise_event(event)
|
|
1516
|
+
when :CHANNEL_PINS_UPDATE
|
|
1517
|
+
event = ChannelPinsUpdateEvent.new(data, self)
|
|
1518
|
+
|
|
1519
|
+
event.channel.process_last_pin_timestamp(data['last_pin_timestamp']) if data.key?('last_pin_timestamp')
|
|
1520
|
+
|
|
1445
1521
|
raise_event(event)
|
|
1446
1522
|
when :GUILD_MEMBER_ADD
|
|
1447
1523
|
add_guild_member(data)
|
|
@@ -1544,6 +1620,10 @@ module Discordrb
|
|
|
1544
1620
|
event = ServerEmojiUpdateEvent.new(server, old_emoji_data[e], new_emoji_data[e], self)
|
|
1545
1621
|
raise_event(event)
|
|
1546
1622
|
end
|
|
1623
|
+
when :APPLICATION_COMMAND_PERMISSIONS_UPDATE
|
|
1624
|
+
event = ApplicationCommandPermissionsUpdateEvent.new(data, self)
|
|
1625
|
+
|
|
1626
|
+
raise_event(event)
|
|
1547
1627
|
when :INTERACTION_CREATE
|
|
1548
1628
|
event = InteractionCreateEvent.new(data, self)
|
|
1549
1629
|
raise_event(event)
|
|
@@ -1552,13 +1632,13 @@ module Discordrb
|
|
|
1552
1632
|
when Interaction::TYPES[:command]
|
|
1553
1633
|
event = ApplicationCommandEvent.new(data, self)
|
|
1554
1634
|
|
|
1555
|
-
Thread.new do
|
|
1556
|
-
Thread.current[:discordrb_name] = "it-#{
|
|
1635
|
+
Thread.new(event) do |evt|
|
|
1636
|
+
Thread.current[:discordrb_name] = "it-#{evt.interaction.id}"
|
|
1557
1637
|
|
|
1558
1638
|
begin
|
|
1559
|
-
debug("Executing application command #{
|
|
1639
|
+
debug("Executing application command #{evt.command_name}:#{evt.command_id}")
|
|
1560
1640
|
|
|
1561
|
-
@application_commands[
|
|
1641
|
+
@application_commands[evt.command_name]&.call(evt)
|
|
1562
1642
|
rescue StandardError => e
|
|
1563
1643
|
log_exception(e)
|
|
1564
1644
|
end
|
|
@@ -1594,6 +1674,10 @@ module Discordrb
|
|
|
1594
1674
|
|
|
1595
1675
|
event = ModalSubmitEvent.new(data, self)
|
|
1596
1676
|
raise_event(event)
|
|
1677
|
+
when Interaction::TYPES[:autocomplete]
|
|
1678
|
+
|
|
1679
|
+
event = AutocompleteEvent.new(data, self)
|
|
1680
|
+
raise_event(event)
|
|
1597
1681
|
end
|
|
1598
1682
|
when :WEBHOOKS_UPDATE
|
|
1599
1683
|
event = WebhookUpdateEvent.new(data, self)
|
|
@@ -1673,15 +1757,15 @@ module Discordrb
|
|
|
1673
1757
|
end
|
|
1674
1758
|
|
|
1675
1759
|
def call_event(handler, event)
|
|
1676
|
-
t = Thread.new do
|
|
1760
|
+
t = Thread.new(event) do |evt|
|
|
1677
1761
|
@event_threads ||= []
|
|
1678
1762
|
@current_thread ||= 0
|
|
1679
1763
|
|
|
1680
1764
|
@event_threads << t
|
|
1681
1765
|
Thread.current[:discordrb_name] = "et-#{@current_thread += 1}"
|
|
1682
1766
|
begin
|
|
1683
|
-
handler.call(
|
|
1684
|
-
handler.after_call(
|
|
1767
|
+
handler.call(evt)
|
|
1768
|
+
handler.after_call(evt)
|
|
1685
1769
|
rescue StandardError => e
|
|
1686
1770
|
log_exception(e)
|
|
1687
1771
|
ensure
|
|
@@ -1692,7 +1776,7 @@ module Discordrb
|
|
|
1692
1776
|
|
|
1693
1777
|
def handle_awaits(event)
|
|
1694
1778
|
@awaits ||= {}
|
|
1695
|
-
@awaits.
|
|
1779
|
+
@awaits.each_value do |await|
|
|
1696
1780
|
key, should_delete = await.match(event)
|
|
1697
1781
|
next unless key
|
|
1698
1782
|
|
|
@@ -1705,6 +1789,8 @@ module Discordrb
|
|
|
1705
1789
|
end
|
|
1706
1790
|
|
|
1707
1791
|
def calculate_intents(intents)
|
|
1792
|
+
intents = [intents] unless intents.is_a? Array
|
|
1793
|
+
|
|
1708
1794
|
intents.reduce(0) do |sum, intent|
|
|
1709
1795
|
case intent
|
|
1710
1796
|
when Symbol
|
data/lib/discordrb/cache.rb
CHANGED
|
@@ -22,6 +22,7 @@ module Discordrb
|
|
|
22
22
|
@channels = {}
|
|
23
23
|
@pm_channels = {}
|
|
24
24
|
@thread_members = {}
|
|
25
|
+
@server_previews = {}
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
# Returns or caches the available voice regions
|
|
@@ -134,6 +135,19 @@ module Discordrb
|
|
|
134
135
|
|
|
135
136
|
alias_method :private_channel, :pm_channel
|
|
136
137
|
|
|
138
|
+
# Get a server preview. If the bot isn't a member of the server, the server must be discoverable.
|
|
139
|
+
# @param id [Integer, String, Server] the ID of the server preview to get.
|
|
140
|
+
# @return [ServerPreview, nil] the server preview, or `nil` if the server isn't accessible.
|
|
141
|
+
def server_preview(id)
|
|
142
|
+
id = id.resolve_id
|
|
143
|
+
return @server_previews[id] if @server_previews[id]
|
|
144
|
+
|
|
145
|
+
response = JSON.parse(API::Server.preview(token, id))
|
|
146
|
+
@server_previews[id] = ServerPreview.new(response, self)
|
|
147
|
+
rescue StandardError
|
|
148
|
+
nil
|
|
149
|
+
end
|
|
150
|
+
|
|
137
151
|
# Ensures a given user object is cached and if not, cache it from the given data hash.
|
|
138
152
|
# @param data [Hash] A data hash representing a user.
|
|
139
153
|
# @return [User] the user represented by the data hash.
|
|
@@ -199,7 +213,7 @@ module Discordrb
|
|
|
199
213
|
# @return [String] Only the code for the invite.
|
|
200
214
|
def resolve_invite_code(invite)
|
|
201
215
|
invite = invite.code if invite.is_a? Discordrb::Invite
|
|
202
|
-
invite = invite[invite.rindex('/') + 1..] if invite.start_with?('http', 'discord.gg')
|
|
216
|
+
invite = invite[(invite.rindex('/') + 1)..] if invite.start_with?('http', 'discord.gg')
|
|
203
217
|
invite
|
|
204
218
|
end
|
|
205
219
|
|
|
@@ -109,7 +109,7 @@ module Discordrb::Commands
|
|
|
109
109
|
spaces_allowed: attributes[:spaces_allowed].nil? ? false : attributes[:spaces_allowed],
|
|
110
110
|
|
|
111
111
|
# Webhooks allowed to trigger commands
|
|
112
|
-
webhook_commands: attributes[:webhook_commands].nil?
|
|
112
|
+
webhook_commands: attributes[:webhook_commands].nil? || attributes[:webhook_commands],
|
|
113
113
|
|
|
114
114
|
channels: attributes[:channels] || [],
|
|
115
115
|
|
|
@@ -153,7 +153,9 @@ module Discordrb::Commands
|
|
|
153
153
|
command = command.aliased_command
|
|
154
154
|
command_name = command.name
|
|
155
155
|
end
|
|
156
|
+
# rubocop:disable Lint/ReturnInVoidContext
|
|
156
157
|
return "The command `#{command_name}` does not exist!" unless command
|
|
158
|
+
# rubocop:enable Lint/ReturnInVoidContext
|
|
157
159
|
|
|
158
160
|
desc = command.attributes[:description] || '*No description available*'
|
|
159
161
|
usage = command.attributes[:usage]
|
|
@@ -257,24 +259,16 @@ module Discordrb::Commands
|
|
|
257
259
|
next arg if types[i].nil? || types[i] == String
|
|
258
260
|
|
|
259
261
|
if types[i] == Integer
|
|
260
|
-
|
|
261
|
-
Integer(arg, 10)
|
|
262
|
-
rescue ArgumentError
|
|
263
|
-
nil
|
|
264
|
-
end
|
|
262
|
+
Integer(arg, 10, exception: false)
|
|
265
263
|
elsif types[i] == Float
|
|
266
|
-
|
|
267
|
-
Float(arg)
|
|
268
|
-
rescue ArgumentError
|
|
269
|
-
nil
|
|
270
|
-
end
|
|
264
|
+
Float(arg, exception: false)
|
|
271
265
|
elsif types[i] == Time
|
|
272
266
|
begin
|
|
273
267
|
Time.parse arg
|
|
274
268
|
rescue ArgumentError
|
|
275
269
|
nil
|
|
276
270
|
end
|
|
277
|
-
elsif
|
|
271
|
+
elsif [TrueClass, FalseClass].include?(types[i])
|
|
278
272
|
if arg.casecmp('true').zero? || arg.downcase.start_with?('y')
|
|
279
273
|
true
|
|
280
274
|
elsif arg.casecmp('false').zero? || arg.downcase.start_with?('n')
|
|
@@ -295,11 +289,7 @@ module Discordrb::Commands
|
|
|
295
289
|
nil
|
|
296
290
|
end
|
|
297
291
|
elsif types[i] == Rational
|
|
298
|
-
|
|
299
|
-
Rational(arg)
|
|
300
|
-
rescue ArgumentError
|
|
301
|
-
nil
|
|
302
|
-
end
|
|
292
|
+
Rational(arg, exception: false)
|
|
303
293
|
elsif types[i] == Range
|
|
304
294
|
begin
|
|
305
295
|
if arg.include? '...'
|
|
@@ -32,10 +32,10 @@ module Discordrb::Commands
|
|
|
32
32
|
channels: attributes[:channels] || nil,
|
|
33
33
|
|
|
34
34
|
# Whether this command is usable in a command chain
|
|
35
|
-
chain_usable: attributes[:chain_usable].nil?
|
|
35
|
+
chain_usable: attributes[:chain_usable].nil? || attributes[:chain_usable],
|
|
36
36
|
|
|
37
37
|
# Whether this command should show up in the help command
|
|
38
|
-
help_available: attributes[:help_available].nil?
|
|
38
|
+
help_available: attributes[:help_available].nil? || attributes[:help_available],
|
|
39
39
|
|
|
40
40
|
# Description (for help command)
|
|
41
41
|
description: attributes[:description] || nil,
|
|
@@ -159,7 +159,7 @@ module Discordrb::Commands
|
|
|
159
159
|
escaped = false
|
|
160
160
|
hacky_delim, hacky_space, hacky_prev, hacky_newline = [0xe001, 0xe002, 0xe003, 0xe004].pack('U*').chars
|
|
161
161
|
|
|
162
|
-
@chain.each_char.
|
|
162
|
+
@chain.each_char.with_index do |char, index|
|
|
163
163
|
# Escape character
|
|
164
164
|
if char == '\\' && !escaped
|
|
165
165
|
escaped = true
|
|
@@ -211,7 +211,7 @@ module Discordrb::Commands
|
|
|
211
211
|
b_level -= 1
|
|
212
212
|
next unless b_level.zero?
|
|
213
213
|
|
|
214
|
-
nested = @chain[b_start + 1..index - 1]
|
|
214
|
+
nested = @chain[(b_start + 1)..(index - 1)]
|
|
215
215
|
subchain = CommandChain.new(nested, @bot, true)
|
|
216
216
|
result += subchain.execute(event)
|
|
217
217
|
end
|
|
@@ -245,8 +245,8 @@ module Discordrb::Commands
|
|
|
245
245
|
command = command.gsub hacky_delim, @attributes[:chain_delimiter]
|
|
246
246
|
|
|
247
247
|
first_space = command.index ' '
|
|
248
|
-
command_name = first_space ? command[0..first_space - 1] : command
|
|
249
|
-
arguments = first_space ? command[first_space + 1..] : ''
|
|
248
|
+
command_name = first_space ? command[0..(first_space - 1)] : command
|
|
249
|
+
arguments = first_space ? command[(first_space + 1)..] : ''
|
|
250
250
|
|
|
251
251
|
# Append a previous sign if none is present
|
|
252
252
|
arguments += @attributes[:previous] unless arguments.include? @attributes[:previous]
|
|
@@ -318,7 +318,7 @@ module Discordrb::Commands
|
|
|
318
318
|
arg.split ' '
|
|
319
319
|
end
|
|
320
320
|
|
|
321
|
-
chain = chain[chain_args_index + 1..]
|
|
321
|
+
chain = chain[(chain_args_index + 1)..]
|
|
322
322
|
end
|
|
323
323
|
|
|
324
324
|
[chain_args, chain]
|