discordrb 3.4.3 → 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/ISSUE_TEMPLATE/bug_report.md +0 -1
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -1
- data/.github/workflows/ci.yml +78 -0
- data/.github/workflows/codeql.yml +65 -0
- data/.github/workflows/deploy.yml +54 -0
- data/.github/workflows/release.yml +45 -0
- data/.markdownlint.json +4 -0
- data/.rubocop.yml +58 -2
- data/CHANGELOG.md +485 -225
- data/LICENSE.txt +1 -1
- data/README.md +38 -26
- data/discordrb-webhooks.gemspec +4 -1
- data/discordrb.gemspec +18 -10
- data/lib/discordrb/api/application.rb +278 -0
- data/lib/discordrb/api/channel.rb +222 -18
- data/lib/discordrb/api/interaction.rb +63 -0
- data/lib/discordrb/api/invite.rb +2 -2
- data/lib/discordrb/api/server.rb +123 -66
- data/lib/discordrb/api/user.rb +20 -5
- data/lib/discordrb/api/webhook.rb +72 -0
- data/lib/discordrb/api.rb +35 -25
- data/lib/discordrb/bot.rb +437 -66
- data/lib/discordrb/cache.rb +41 -22
- data/lib/discordrb/commands/command_bot.rb +13 -21
- data/lib/discordrb/commands/container.rb +1 -1
- data/lib/discordrb/commands/parser.rb +7 -7
- data/lib/discordrb/commands/rate_limiter.rb +1 -1
- data/lib/discordrb/container.rb +178 -3
- data/lib/discordrb/data/activity.rb +1 -1
- data/lib/discordrb/data/application.rb +1 -0
- data/lib/discordrb/data/attachment.rb +38 -3
- data/lib/discordrb/data/audit_logs.rb +3 -3
- data/lib/discordrb/data/avatar_decoration.rb +26 -0
- data/lib/discordrb/data/call.rb +22 -0
- data/lib/discordrb/data/channel.rb +299 -30
- data/lib/discordrb/data/collectibles.rb +45 -0
- data/lib/discordrb/data/component.rb +229 -0
- data/lib/discordrb/data/embed.rb +10 -3
- data/lib/discordrb/data/emoji.rb +20 -1
- data/lib/discordrb/data/integration.rb +45 -3
- data/lib/discordrb/data/interaction.rb +937 -0
- data/lib/discordrb/data/invite.rb +1 -1
- data/lib/discordrb/data/member.rb +236 -44
- data/lib/discordrb/data/message.rb +278 -51
- data/lib/discordrb/data/overwrite.rb +15 -7
- 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 +204 -18
- data/lib/discordrb/data/server.rb +194 -118
- data/lib/discordrb/data/server_preview.rb +68 -0
- data/lib/discordrb/data/snapshot.rb +110 -0
- data/lib/discordrb/data/user.rb +132 -12
- data/lib/discordrb/data/voice_region.rb +1 -0
- data/lib/discordrb/data/webhook.rb +99 -9
- data/lib/discordrb/data.rb +9 -0
- data/lib/discordrb/errors.rb +47 -3
- data/lib/discordrb/events/await.rb +1 -1
- data/lib/discordrb/events/channels.rb +38 -1
- data/lib/discordrb/events/generic.rb +2 -0
- data/lib/discordrb/events/guilds.rb +6 -1
- data/lib/discordrb/events/interactions.rb +575 -0
- data/lib/discordrb/events/invites.rb +2 -0
- data/lib/discordrb/events/members.rb +19 -2
- data/lib/discordrb/events/message.rb +42 -8
- data/lib/discordrb/events/presence.rb +23 -14
- data/lib/discordrb/events/raw.rb +1 -0
- data/lib/discordrb/events/reactions.rb +2 -1
- data/lib/discordrb/events/roles.rb +2 -0
- data/lib/discordrb/events/threads.rb +100 -0
- 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 +57 -28
- data/lib/discordrb/paginator.rb +3 -3
- data/lib/discordrb/permissions.rb +71 -35
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +2 -2
- data/lib/discordrb/voice/network.rb +18 -7
- data/lib/discordrb/voice/sodium.rb +3 -1
- data/lib/discordrb/voice/voice_bot.rb +3 -3
- data/lib/discordrb/webhooks.rb +2 -0
- data/lib/discordrb/websocket.rb +0 -10
- data/lib/discordrb.rb +54 -5
- metadata +87 -25
- data/.circleci/config.yml +0 -126
- data/.codeclimate.yml +0 -16
- data/.travis.yml +0 -32
- data/bin/travis_build_docs.sh +0 -17
|
@@ -17,9 +17,11 @@ module Discordrb::Events
|
|
|
17
17
|
# @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
|
|
18
18
|
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
|
|
19
19
|
# @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
|
|
20
|
+
# @param components [View, Array<Hash>, nil] A collection of components to attach to the message.
|
|
21
|
+
# @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2) and SUPPRESS_NOTIFICATIONS (1 << 12) can be set.
|
|
20
22
|
# @return [Discordrb::Message] the message that was sent
|
|
21
|
-
def send_message(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil)
|
|
22
|
-
channel.send_message(content, tts, embed, attachments, allowed_mentions, message_reference)
|
|
23
|
+
def send_message(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0)
|
|
24
|
+
channel.send_message(content, tts, embed, attachments, allowed_mentions, message_reference, components, flags)
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
# The same as {#send_message}, but yields a {Webhooks::Embed} for easy building of embedded content inside a block.
|
|
@@ -30,11 +32,13 @@ module Discordrb::Events
|
|
|
30
32
|
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
|
|
31
33
|
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
|
|
32
34
|
# @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
|
|
35
|
+
# @param components [View, Array<Hash>, nil] A collection of components to attach to the message.
|
|
36
|
+
# @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2) and SUPPRESS_NOTIFICATIONS (1 << 12) can be set.
|
|
33
37
|
# @yield [embed] Yields the embed to allow for easy building inside a block.
|
|
34
38
|
# @yieldparam embed [Discordrb::Webhooks::Embed] The embed from the parameters, or a new one.
|
|
35
39
|
# @return [Message] The resulting message.
|
|
36
|
-
def send_embed(message = '', embed = nil, attachments = nil, tts = false, allowed_mentions = nil, message_reference = nil, &block)
|
|
37
|
-
channel.send_embed(message, embed, attachments, tts, allowed_mentions, message_reference, &block)
|
|
40
|
+
def send_embed(message = '', embed = nil, attachments = nil, tts = false, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0, &block)
|
|
41
|
+
channel.send_embed(message, embed, attachments, tts, allowed_mentions, message_reference, components, flags, &block)
|
|
38
42
|
end
|
|
39
43
|
|
|
40
44
|
# Sends a temporary message to the channel this message was sent in, right now.
|
|
@@ -44,8 +48,16 @@ module Discordrb::Events
|
|
|
44
48
|
# @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
|
|
45
49
|
# @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
|
|
46
50
|
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
# @param components [View, Array<Hash>, nil] A collection of components to attach to the message.
|
|
52
|
+
# @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2) and SUPPRESS_NOTIFICATIONS (1 << 12) can be set.
|
|
53
|
+
def send_temporary_message(content, timeout, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, components = nil, flags = 0)
|
|
54
|
+
channel.send_temporary_message(content, timeout, tts, embed, attachments, allowed_mentions, components, flags)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Sends a message to the channel this message was sent in, right now.
|
|
58
|
+
# @see Channel#send_message!
|
|
59
|
+
def send_message!(...)
|
|
60
|
+
channel.send_message!(...)
|
|
49
61
|
end
|
|
50
62
|
|
|
51
63
|
# Adds a string to be sent after the event has finished execution. Avoids problems with rate limiting because only
|
|
@@ -81,6 +93,9 @@ module Discordrb::Events
|
|
|
81
93
|
alias_method :send, :send_message
|
|
82
94
|
alias_method :respond, :send_message
|
|
83
95
|
alias_method :send_temp, :send_temporary_message
|
|
96
|
+
|
|
97
|
+
alias_method :send!, :send_message!
|
|
98
|
+
alias_method :respond!, :send_message!
|
|
84
99
|
end
|
|
85
100
|
|
|
86
101
|
# Event raised when a text message is sent to a channel
|
|
@@ -121,6 +136,7 @@ module Discordrb::Events
|
|
|
121
136
|
# @see Channel#server
|
|
122
137
|
delegate :server, to: :channel
|
|
123
138
|
|
|
139
|
+
# @!visibility private
|
|
124
140
|
def initialize(message, bot)
|
|
125
141
|
@bot = bot
|
|
126
142
|
@message = message
|
|
@@ -223,7 +239,9 @@ module Discordrb::Events
|
|
|
223
239
|
a == e
|
|
224
240
|
end
|
|
225
241
|
end,
|
|
226
|
-
matches_all(@attributes[:from], event.
|
|
242
|
+
matches_all(@attributes[:from], event.message) do |a, e|
|
|
243
|
+
# Resolve the author in the block in order to prevent resolving the author even when the attribute is `nil`
|
|
244
|
+
e = e.author
|
|
227
245
|
case a
|
|
228
246
|
when String
|
|
229
247
|
a == e.name
|
|
@@ -244,9 +262,18 @@ module Discordrb::Events
|
|
|
244
262
|
match ? (e == match[0]) : false
|
|
245
263
|
end
|
|
246
264
|
end,
|
|
265
|
+
matches_all(@attributes[:type] || @attributes[:message_type], event.message.type) do |a, e|
|
|
266
|
+
case a
|
|
267
|
+
when String, Symbol
|
|
268
|
+
Discordrb::Message::TYPES[a.to_sym] == e
|
|
269
|
+
when Integer
|
|
270
|
+
a == e
|
|
271
|
+
end
|
|
272
|
+
end,
|
|
247
273
|
matches_all(@attributes[:after], event.timestamp) { |a, e| a > e },
|
|
248
274
|
matches_all(@attributes[:before], event.timestamp) { |a, e| a < e },
|
|
249
|
-
matches_all(@attributes[:private], event.channel.private?) { |a, e| !e == !a }
|
|
275
|
+
matches_all(@attributes[:private], event.channel.private?) { |a, e| !e == !a },
|
|
276
|
+
matches_all(@attributes[:server], event.server) { |a, e| a&.resolve_id == e&.resolve_id }
|
|
250
277
|
].reduce(true, &:&)
|
|
251
278
|
end
|
|
252
279
|
|
|
@@ -279,10 +306,14 @@ module Discordrb::Events
|
|
|
279
306
|
# @return [Integer] the ID associated with this event
|
|
280
307
|
attr_reader :id
|
|
281
308
|
|
|
309
|
+
# @return [Server, nil] the server associated with this event
|
|
310
|
+
attr_reader :server
|
|
311
|
+
|
|
282
312
|
# @!visibility private
|
|
283
313
|
def initialize(data, bot)
|
|
284
314
|
@id = data['id'].to_i
|
|
285
315
|
@channel = bot.channel(data['channel_id'].to_i)
|
|
316
|
+
@server = @channel.server
|
|
286
317
|
@saved_message = ''
|
|
287
318
|
@bot = bot
|
|
288
319
|
end
|
|
@@ -308,6 +339,9 @@ module Discordrb::Events
|
|
|
308
339
|
else
|
|
309
340
|
a == e
|
|
310
341
|
end
|
|
342
|
+
end,
|
|
343
|
+
matches_all(@attributes[:server], event.server) do |a, e|
|
|
344
|
+
a&.resolve_id == e&.resolve_id
|
|
311
345
|
end
|
|
312
346
|
].reduce(true, &:&)
|
|
313
347
|
end
|
|
@@ -19,6 +19,7 @@ module Discordrb::Events
|
|
|
19
19
|
# on various device types (`:desktop`, `:mobile`, or `:web`). The value will be `nil` if the user is offline or invisible.
|
|
20
20
|
attr_reader :client_status
|
|
21
21
|
|
|
22
|
+
# @!visibility private
|
|
22
23
|
def initialize(data, bot)
|
|
23
24
|
@bot = bot
|
|
24
25
|
|
|
@@ -65,28 +66,36 @@ module Discordrb::Events
|
|
|
65
66
|
# @return [User] the user whose status got updated.
|
|
66
67
|
attr_reader :user
|
|
67
68
|
|
|
68
|
-
# @return [
|
|
69
|
-
attr_reader :
|
|
69
|
+
# @return [Discordrb::Activity] The new activity
|
|
70
|
+
attr_reader :activity
|
|
70
71
|
|
|
71
|
-
#
|
|
72
|
-
|
|
72
|
+
# @!attribute [r] url
|
|
73
|
+
# @return [String] the URL to the stream
|
|
73
74
|
|
|
74
|
-
#
|
|
75
|
-
|
|
75
|
+
# @!attribute [r] details
|
|
76
|
+
# @return [String] what the player is currently doing (ex. game being streamed)
|
|
76
77
|
|
|
77
|
-
#
|
|
78
|
-
|
|
78
|
+
# @!attribute [r] type
|
|
79
|
+
# @return [Integer] the type of play. See {Discordrb::Activity}
|
|
80
|
+
delegate :url, :details, :type, to: :activity
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
# @return [Hash<Symbol, Symbol>] the current online status (`:online`, `:idle` or `:dnd`) of the user
|
|
83
|
+
# on various device types (`:desktop`, `:mobile`, or `:web`). The value will be `nil` if the user is offline or invisible.
|
|
84
|
+
attr_reader :client_status
|
|
85
|
+
|
|
86
|
+
# @!visibility private
|
|
87
|
+
def initialize(data, activity, bot)
|
|
81
88
|
@bot = bot
|
|
89
|
+
@activity = activity
|
|
82
90
|
|
|
83
91
|
@server = bot.server(data['guild_id'].to_i)
|
|
84
92
|
@user = bot.user(data['user']['id'].to_i)
|
|
85
|
-
@
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
@client_status = @user.client_status
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @return [String] the name of the new game the user is playing.
|
|
97
|
+
def game
|
|
98
|
+
@activity.name
|
|
90
99
|
end
|
|
91
100
|
end
|
|
92
101
|
|
data/lib/discordrb/events/raw.rb
CHANGED
|
@@ -14,6 +14,7 @@ module Discordrb::Events
|
|
|
14
14
|
# @!visibility private
|
|
15
15
|
attr_reader :message_id
|
|
16
16
|
|
|
17
|
+
# @!visibility private
|
|
17
18
|
def initialize(data, bot)
|
|
18
19
|
@bot = bot
|
|
19
20
|
|
|
@@ -113,6 +114,7 @@ module Discordrb::Events
|
|
|
113
114
|
# @!visibility private
|
|
114
115
|
attr_reader :message_id
|
|
115
116
|
|
|
117
|
+
# @!visibility private
|
|
116
118
|
def initialize(data, bot)
|
|
117
119
|
@bot = bot
|
|
118
120
|
|
|
@@ -137,7 +139,6 @@ module Discordrb::Events
|
|
|
137
139
|
# Check for the proper event type
|
|
138
140
|
return false unless event.is_a? ReactionRemoveAllEvent
|
|
139
141
|
|
|
140
|
-
# No attributes yet as there is no property available on the event that doesn't involve doing a resolution request
|
|
141
142
|
[
|
|
142
143
|
matches_all(@attributes[:message], event.message_id) do |a, e|
|
|
143
144
|
a == e
|
|
@@ -17,6 +17,7 @@ module Discordrb::Events
|
|
|
17
17
|
# @see Role#name
|
|
18
18
|
delegate :name, to: :role
|
|
19
19
|
|
|
20
|
+
# @!visibility private
|
|
20
21
|
def initialize(data, bot)
|
|
21
22
|
@bot = bot
|
|
22
23
|
|
|
@@ -54,6 +55,7 @@ module Discordrb::Events
|
|
|
54
55
|
# @return [Server] the server on which a role got deleted.
|
|
55
56
|
attr_reader :server
|
|
56
57
|
|
|
58
|
+
# @!visibility private
|
|
57
59
|
def initialize(data, bot)
|
|
58
60
|
@bot = bot
|
|
59
61
|
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Generic subclass for threads
|
|
4
|
+
module Discordrb::Events
|
|
5
|
+
# Raised when a thread is created
|
|
6
|
+
class ThreadCreateEvent < Event
|
|
7
|
+
# @return [Channel] the thread in question.
|
|
8
|
+
attr_reader :thread
|
|
9
|
+
|
|
10
|
+
delegate :name, :server, :owner, :parent_channel, :thread_metadata, to: :thread
|
|
11
|
+
|
|
12
|
+
# @!visibility private
|
|
13
|
+
def initialize(data, bot)
|
|
14
|
+
@bot = bot
|
|
15
|
+
@thread = data.is_a?(Discordrb::Channel) ? data : bot.channel(data['id'].to_i)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Event handler for ChannelCreateEvent
|
|
20
|
+
class ThreadCreateEventHandler < EventHandler
|
|
21
|
+
def matches?(event)
|
|
22
|
+
# Check for the proper event type
|
|
23
|
+
return false unless event.is_a? ThreadCreateEvent
|
|
24
|
+
|
|
25
|
+
[
|
|
26
|
+
matches_all(@attributes[:name], event.name) do |a, e|
|
|
27
|
+
a == if a.is_a? String
|
|
28
|
+
e.to_s
|
|
29
|
+
else
|
|
30
|
+
e
|
|
31
|
+
end
|
|
32
|
+
end,
|
|
33
|
+
matches_all(@attributes[:server], event.server) do |a, e|
|
|
34
|
+
a.resolve_id == e.resolve_id
|
|
35
|
+
end,
|
|
36
|
+
matches_all(@attributes[:invitable], event.thread.invitable) do |a, e|
|
|
37
|
+
a == e
|
|
38
|
+
end,
|
|
39
|
+
matches_all(@attributes[:owner], event.thread.owner) do |a, e|
|
|
40
|
+
a.resolve_id == e.resolve_id
|
|
41
|
+
end,
|
|
42
|
+
matches_all(@attributes[:channel], event.thread.parent) do |a, e|
|
|
43
|
+
a.resolve_id == e.resolve_id
|
|
44
|
+
end
|
|
45
|
+
].reduce(true, &:&)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Raised when a thread is updated (e.g. name changes)
|
|
50
|
+
class ThreadUpdateEvent < ThreadCreateEvent; end
|
|
51
|
+
|
|
52
|
+
# Event handler for ThreadUpdateEvent
|
|
53
|
+
class ThreadUpdateEventHandler < ThreadCreateEventHandler
|
|
54
|
+
def matches?(event)
|
|
55
|
+
# Check for the proper event type
|
|
56
|
+
return false unless event.is_a? ThreadUpdateEvent
|
|
57
|
+
|
|
58
|
+
super
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Raised when members are added or removed from a thread.
|
|
63
|
+
class ThreadMembersUpdateEvent < Event
|
|
64
|
+
# @return [Channel]
|
|
65
|
+
attr_reader :thread
|
|
66
|
+
|
|
67
|
+
# @return [Array<Integer>]
|
|
68
|
+
attr_reader :removed_member_ids
|
|
69
|
+
|
|
70
|
+
# @return [Integer]
|
|
71
|
+
attr_reader :member_count
|
|
72
|
+
|
|
73
|
+
delegate :name, :server, :owner, :parent_channel, :thread_metadata, to: :thread
|
|
74
|
+
|
|
75
|
+
# @!visibility private
|
|
76
|
+
def initialize(data, bot)
|
|
77
|
+
@bot = bot
|
|
78
|
+
@server = bot.server(data['guild_id'].to_i) if data['guild_id']
|
|
79
|
+
@thread = data.is_a?(Discordrb::Channel) ? data : bot.channel(data['id'].to_i)
|
|
80
|
+
@added_member_ids = data['added_members']&.map { |m| m['user_id']&.to_i } || []
|
|
81
|
+
@removed_member_ids = data['removed_member_ids']&.map(&:resolve_id) || []
|
|
82
|
+
@member_count = data['member_count']
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @return [Array<Member, User>] the members that were added to the thread
|
|
87
|
+
def added_members
|
|
88
|
+
@added_members ||= @added_member_ids&.map { |id| @server&.member(id) || @bot.user(id) }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Event handler for ThreadMembersUpdateEvent
|
|
92
|
+
class ThreadMembersUpdateEventHandler < ThreadCreateEventHandler
|
|
93
|
+
def matches?(event)
|
|
94
|
+
# Check for the proper event type
|
|
95
|
+
return false unless event.is_a? ThreadMembersUpdateEvent
|
|
96
|
+
|
|
97
|
+
super
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
data/lib/discordrb/gateway.rb
CHANGED
|
@@ -90,14 +90,22 @@ module Discordrb
|
|
|
90
90
|
# This class stores the data of an active gateway session. Note that this is different from a websocket connection -
|
|
91
91
|
# there may be multiple sessions per connection or one session may persist over multiple connections.
|
|
92
92
|
class Session
|
|
93
|
+
# @return [String] Used to uniquely identify this session. Mostly used when resuming connections.
|
|
93
94
|
attr_reader :session_id
|
|
95
|
+
|
|
96
|
+
# @return [Integer] Incrementing integer used to determine the most recent event reccived from Discord.
|
|
94
97
|
attr_accessor :sequence
|
|
95
98
|
|
|
96
|
-
|
|
99
|
+
# @return [String] Gateway URL used to reconnect to the gateway node that Discord wants this session to use.
|
|
100
|
+
attr_reader :resume_gateway_url
|
|
101
|
+
|
|
102
|
+
# @!visibility private
|
|
103
|
+
def initialize(session_id, resume_gateway_url)
|
|
97
104
|
@session_id = session_id
|
|
98
105
|
@sequence = 0
|
|
99
106
|
@suspended = false
|
|
100
107
|
@invalid = false
|
|
108
|
+
@resume_gateway_url = resume_gateway_url
|
|
101
109
|
end
|
|
102
110
|
|
|
103
111
|
# Flags this session as suspended, so we know not to try and send heartbeats, etc. to the gateway until we've reconnected
|
|
@@ -117,6 +125,7 @@ module Discordrb
|
|
|
117
125
|
# Flags this session as being invalid
|
|
118
126
|
def invalidate
|
|
119
127
|
@invalid = true
|
|
128
|
+
@resume_gateway_url = nil
|
|
120
129
|
end
|
|
121
130
|
|
|
122
131
|
def invalid?
|
|
@@ -134,7 +143,14 @@ module Discordrb
|
|
|
134
143
|
LARGE_THRESHOLD = 100
|
|
135
144
|
|
|
136
145
|
# The version of the gateway that's supposed to be used.
|
|
137
|
-
GATEWAY_VERSION =
|
|
146
|
+
GATEWAY_VERSION = 9
|
|
147
|
+
|
|
148
|
+
# Close codes that are unrecoverable, after which we should not try to reconnect.
|
|
149
|
+
# - 4003: Not authenticated. How did this happen?
|
|
150
|
+
# - 4004: Authentication failed. Token was wrong, nothing we can do.
|
|
151
|
+
# - 4011: Sharding required. Currently requires developer intervention.
|
|
152
|
+
# - 4014: Use of disabled privileged intents.
|
|
153
|
+
FATAL_CLOSE_CODES = [4003, 4004, 4011, 4014].freeze
|
|
138
154
|
|
|
139
155
|
# Heartbeat ACKs are Discord's way of verifying on the client side whether the connection is still alive. If this is
|
|
140
156
|
# set to true (default value) the gateway client will use that functionality to detect zombie connections and
|
|
@@ -143,7 +159,10 @@ module Discordrb
|
|
|
143
159
|
# @return [true, false] whether or not this gateway should check for heartbeat ACKs.
|
|
144
160
|
attr_accessor :check_heartbeat_acks
|
|
145
161
|
|
|
146
|
-
|
|
162
|
+
# @return [Integer] the intent parameter sent to the gateway server.
|
|
163
|
+
attr_reader :intents
|
|
164
|
+
|
|
165
|
+
def initialize(bot, token, shard_key = nil, compress_mode = :stream, intents = ALL_INTENTS)
|
|
147
166
|
@token = token
|
|
148
167
|
@bot = bot
|
|
149
168
|
|
|
@@ -277,12 +296,12 @@ module Discordrb
|
|
|
277
296
|
def identify
|
|
278
297
|
compress = @compress_mode == :large
|
|
279
298
|
send_identify(@token, {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}, compress,
|
|
299
|
+
os: RUBY_PLATFORM,
|
|
300
|
+
browser: 'discordrb',
|
|
301
|
+
device: 'discordrb',
|
|
302
|
+
referrer: '',
|
|
303
|
+
referring_domain: ''
|
|
304
|
+
}, compress, LARGE_THRESHOLD, @shard_key, @intents)
|
|
286
305
|
end
|
|
287
306
|
|
|
288
307
|
# Sends an identify packet (op 2). This starts a new session on the current connection and tells Discord who we are.
|
|
@@ -292,26 +311,26 @@ module Discordrb
|
|
|
292
311
|
# @param properties [Hash<Symbol => String>] A list of properties for Discord to use in analytics. The following
|
|
293
312
|
# keys are recognised:
|
|
294
313
|
#
|
|
295
|
-
# - "
|
|
296
|
-
# - "
|
|
297
|
-
# - "
|
|
298
|
-
# - "
|
|
299
|
-
# - "
|
|
314
|
+
# - "os" (recommended value: the operating system the bot is running on)
|
|
315
|
+
# - "browser" (recommended value: library name)
|
|
316
|
+
# - "device" (recommended value: library name)
|
|
317
|
+
# - "referrer" (recommended value: empty)
|
|
318
|
+
# - "referring_domain" (recommended value: empty)
|
|
300
319
|
#
|
|
301
320
|
# @param compress [true, false] Whether certain large packets should be compressed using zlib.
|
|
302
321
|
# @param large_threshold [Integer] The member threshold after which a server counts as large and will have to have
|
|
303
322
|
# its member list chunked.
|
|
304
323
|
# @param shard_key [Array(Integer, Integer), nil] The shard key to use for sharding, represented as
|
|
305
324
|
# [shard_id, num_shards], or nil if the bot should not be sharded.
|
|
306
|
-
def send_identify(token, properties, compress, large_threshold, shard_key = nil)
|
|
325
|
+
def send_identify(token, properties, compress, large_threshold, shard_key = nil, intents = ALL_INTENTS)
|
|
307
326
|
data = {
|
|
308
327
|
# Don't send a v anymore as it's entirely determined by the URL now
|
|
309
328
|
token: token,
|
|
310
329
|
properties: properties,
|
|
311
330
|
compress: compress,
|
|
312
|
-
large_threshold: large_threshold
|
|
331
|
+
large_threshold: large_threshold,
|
|
332
|
+
intents: intents
|
|
313
333
|
}
|
|
314
|
-
data[:intents] = @intents unless @intents.nil?
|
|
315
334
|
|
|
316
335
|
# Don't include the shard key at all if it is nil as Discord checks for its mere existence
|
|
317
336
|
data[:shard] = shard_key if shard_key
|
|
@@ -449,8 +468,13 @@ module Discordrb
|
|
|
449
468
|
# suspended (e.g. after op7)
|
|
450
469
|
if (@session && !@session.suspended?) || !@session
|
|
451
470
|
sleep @heartbeat_interval
|
|
452
|
-
|
|
453
|
-
|
|
471
|
+
# Check if we're connected here, since we could possibly be waiting for a reconnect to occur.
|
|
472
|
+
if @handshaked && !@closed
|
|
473
|
+
@bot.raise_heartbeat_event
|
|
474
|
+
heartbeat
|
|
475
|
+
else
|
|
476
|
+
LOGGER.debug('Tried to send a heartbeat without being connected! Ignoring, we should be fine.')
|
|
477
|
+
end
|
|
454
478
|
else
|
|
455
479
|
sleep 1
|
|
456
480
|
end
|
|
@@ -535,7 +559,7 @@ module Discordrb
|
|
|
535
559
|
end
|
|
536
560
|
|
|
537
561
|
def process_gateway
|
|
538
|
-
raw_url = find_gateway
|
|
562
|
+
raw_url = @session&.resume_gateway_url || find_gateway
|
|
539
563
|
|
|
540
564
|
# Append a slash in case it's not there (I'm not sure how well WSCS handles it otherwise)
|
|
541
565
|
raw_url += '/' unless raw_url.end_with? '/'
|
|
@@ -653,7 +677,9 @@ module Discordrb
|
|
|
653
677
|
LOGGER.log_exception(e)
|
|
654
678
|
end
|
|
655
679
|
|
|
680
|
+
# rubocop:disable Lint/UselessConstantScoping
|
|
656
681
|
ZLIB_SUFFIX = "\x00\x00\xFF\xFF".b.freeze
|
|
682
|
+
# rubocop:enable Lint/UselessConstantScoping
|
|
657
683
|
|
|
658
684
|
def handle_message(msg)
|
|
659
685
|
case @compress_mode
|
|
@@ -713,9 +739,9 @@ module Discordrb
|
|
|
713
739
|
when :READY
|
|
714
740
|
LOGGER.info("Discord using gateway protocol version: #{data['v']}, requested: #{GATEWAY_VERSION}")
|
|
715
741
|
|
|
716
|
-
@session = Session.new(data['session_id'])
|
|
742
|
+
@session = Session.new(data['session_id'], data['resume_gateway_url'])
|
|
717
743
|
@session.sequence = 0
|
|
718
|
-
@bot.__send__(:notify_ready) if @intents &&
|
|
744
|
+
@bot.__send__(:notify_ready) if @intents && @intents.nobits?(INTENTS[:servers])
|
|
719
745
|
when :RESUMED
|
|
720
746
|
# The RESUMED event is received after a successful op 6 (resume). It does nothing except tell the bot the
|
|
721
747
|
# connection is initiated (like READY would). Starting with v5, it doesn't set a new heartbeat interval anymore
|
|
@@ -786,12 +812,6 @@ module Discordrb
|
|
|
786
812
|
handle_close(e)
|
|
787
813
|
end
|
|
788
814
|
|
|
789
|
-
# Close codes that are unrecoverable, after which we should not try to reconnect.
|
|
790
|
-
# - 4003: Not authenticated. How did this happen?
|
|
791
|
-
# - 4004: Authentication failed. Token was wrong, nothing we can do.
|
|
792
|
-
# - 4011: Sharding required. Currently requires developer intervention.
|
|
793
|
-
FATAL_CLOSE_CODES = [4003, 4004, 4011].freeze
|
|
794
|
-
|
|
795
815
|
def handle_close(e)
|
|
796
816
|
@bot.__send__(:raise_event, Events::DisconnectEvent.new(@bot))
|
|
797
817
|
|
|
@@ -800,6 +820,15 @@ module Discordrb
|
|
|
800
820
|
LOGGER.error('Websocket close frame received!')
|
|
801
821
|
LOGGER.error("Code: #{e.code}")
|
|
802
822
|
LOGGER.error("Message: #{e.data}")
|
|
823
|
+
|
|
824
|
+
if e.code == 4014
|
|
825
|
+
LOGGER.error(<<~ERROR)
|
|
826
|
+
You attempted to identify with privileged intents that your bot is not authorized to use
|
|
827
|
+
Please enable the privileged intents on the bot page of your application on the discord developer page.
|
|
828
|
+
Read more here https://discord.com/developers/docs/topics/gateway#privileged-intents
|
|
829
|
+
ERROR
|
|
830
|
+
end
|
|
831
|
+
|
|
803
832
|
@should_reconnect = false if FATAL_CLOSE_CODES.include?(e.code)
|
|
804
833
|
elsif e.is_a? Exception
|
|
805
834
|
# Log the exception
|
data/lib/discordrb/paginator.rb
CHANGED
|
@@ -24,7 +24,7 @@ module Discordrb
|
|
|
24
24
|
# no more results or the configured `limit` is reached.
|
|
25
25
|
def each
|
|
26
26
|
last_page = nil
|
|
27
|
-
until
|
|
27
|
+
until limit_exceeded?
|
|
28
28
|
page = @block.call(last_page)
|
|
29
29
|
return if page.empty?
|
|
30
30
|
|
|
@@ -38,7 +38,7 @@ module Discordrb
|
|
|
38
38
|
enumerator.each do |item|
|
|
39
39
|
yield item
|
|
40
40
|
@count += 1
|
|
41
|
-
break if
|
|
41
|
+
break if limit_exceeded?
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
last_page = page
|
|
@@ -48,7 +48,7 @@ module Discordrb
|
|
|
48
48
|
private
|
|
49
49
|
|
|
50
50
|
# Whether the paginator limit has been exceeded
|
|
51
|
-
def
|
|
51
|
+
def limit_exceeded?
|
|
52
52
|
return false if @limit.nil?
|
|
53
53
|
|
|
54
54
|
@count >= @limit
|