discordrb 3.3.0 → 3.5.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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +152 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  5. data/.github/pull_request_template.md +37 -0
  6. data/.github/workflows/codeql.yml +65 -0
  7. data/.markdownlint.json +4 -0
  8. data/.rubocop.yml +39 -36
  9. data/CHANGELOG.md +874 -552
  10. data/Gemfile +2 -0
  11. data/LICENSE.txt +1 -1
  12. data/README.md +80 -86
  13. data/Rakefile +2 -0
  14. data/bin/console +1 -0
  15. data/discordrb-webhooks.gemspec +9 -6
  16. data/discordrb.gemspec +21 -18
  17. data/lib/discordrb/allowed_mentions.rb +36 -0
  18. data/lib/discordrb/api/application.rb +202 -0
  19. data/lib/discordrb/api/channel.rb +236 -47
  20. data/lib/discordrb/api/interaction.rb +54 -0
  21. data/lib/discordrb/api/invite.rb +5 -5
  22. data/lib/discordrb/api/server.rb +94 -66
  23. data/lib/discordrb/api/user.rb +17 -11
  24. data/lib/discordrb/api/webhook.rb +63 -6
  25. data/lib/discordrb/api.rb +55 -16
  26. data/lib/discordrb/await.rb +0 -1
  27. data/lib/discordrb/bot.rb +480 -93
  28. data/lib/discordrb/cache.rb +31 -24
  29. data/lib/discordrb/colour_rgb.rb +43 -0
  30. data/lib/discordrb/commands/command_bot.rb +35 -12
  31. data/lib/discordrb/commands/container.rb +21 -24
  32. data/lib/discordrb/commands/parser.rb +20 -20
  33. data/lib/discordrb/commands/rate_limiter.rb +4 -3
  34. data/lib/discordrb/container.rb +209 -20
  35. data/lib/discordrb/data/activity.rb +271 -0
  36. data/lib/discordrb/data/application.rb +50 -0
  37. data/lib/discordrb/data/attachment.rb +71 -0
  38. data/lib/discordrb/data/audit_logs.rb +345 -0
  39. data/lib/discordrb/data/channel.rb +993 -0
  40. data/lib/discordrb/data/component.rb +229 -0
  41. data/lib/discordrb/data/embed.rb +251 -0
  42. data/lib/discordrb/data/emoji.rb +82 -0
  43. data/lib/discordrb/data/integration.rb +122 -0
  44. data/lib/discordrb/data/interaction.rb +800 -0
  45. data/lib/discordrb/data/invite.rb +137 -0
  46. data/lib/discordrb/data/member.rb +372 -0
  47. data/lib/discordrb/data/message.rb +414 -0
  48. data/lib/discordrb/data/overwrite.rb +108 -0
  49. data/lib/discordrb/data/profile.rb +91 -0
  50. data/lib/discordrb/data/reaction.rb +33 -0
  51. data/lib/discordrb/data/recipient.rb +34 -0
  52. data/lib/discordrb/data/role.rb +248 -0
  53. data/lib/discordrb/data/server.rb +1004 -0
  54. data/lib/discordrb/data/user.rb +264 -0
  55. data/lib/discordrb/data/voice_region.rb +45 -0
  56. data/lib/discordrb/data/voice_state.rb +41 -0
  57. data/lib/discordrb/data/webhook.rb +238 -0
  58. data/lib/discordrb/data.rb +28 -4180
  59. data/lib/discordrb/errors.rb +46 -4
  60. data/lib/discordrb/events/bans.rb +7 -5
  61. data/lib/discordrb/events/channels.rb +3 -1
  62. data/lib/discordrb/events/guilds.rb +16 -9
  63. data/lib/discordrb/events/interactions.rb +482 -0
  64. data/lib/discordrb/events/invites.rb +125 -0
  65. data/lib/discordrb/events/members.rb +6 -2
  66. data/lib/discordrb/events/message.rb +72 -27
  67. data/lib/discordrb/events/presence.rb +35 -18
  68. data/lib/discordrb/events/raw.rb +1 -3
  69. data/lib/discordrb/events/reactions.rb +49 -4
  70. data/lib/discordrb/events/threads.rb +96 -0
  71. data/lib/discordrb/events/typing.rb +6 -4
  72. data/lib/discordrb/events/voice_server_update.rb +47 -0
  73. data/lib/discordrb/events/voice_state_update.rb +15 -10
  74. data/lib/discordrb/events/webhooks.rb +9 -6
  75. data/lib/discordrb/gateway.rb +99 -71
  76. data/lib/discordrb/id_object.rb +39 -0
  77. data/lib/discordrb/light/integrations.rb +1 -1
  78. data/lib/discordrb/light/light_bot.rb +1 -1
  79. data/lib/discordrb/logger.rb +4 -4
  80. data/lib/discordrb/paginator.rb +57 -0
  81. data/lib/discordrb/permissions.rb +159 -39
  82. data/lib/discordrb/version.rb +1 -1
  83. data/lib/discordrb/voice/encoder.rb +16 -7
  84. data/lib/discordrb/voice/network.rb +99 -47
  85. data/lib/discordrb/voice/sodium.rb +98 -0
  86. data/lib/discordrb/voice/voice_bot.rb +33 -25
  87. data/lib/discordrb/webhooks.rb +2 -0
  88. data/lib/discordrb.rb +107 -1
  89. metadata +126 -54
  90. data/.codeclimate.yml +0 -16
  91. data/.travis.yml +0 -33
  92. data/bin/travis_build_docs.sh +0 -17
  93. /data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +0 -0
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb::Events
4
+ # Raised when an invite is created.
5
+ class InviteCreateEvent < Event
6
+ # @return [Invite] The invite that was created.
7
+ attr_reader :invite
8
+
9
+ # @return [Server, nil] The server the invite was created for.
10
+ attr_reader :server
11
+
12
+ # @return [Channel] The channel the invite was created for.
13
+ attr_reader :channel
14
+
15
+ # @!attribute [r] code
16
+ # @return [String] The code for the created invite.
17
+ # @see Invite#code
18
+ # @!attribute [r] created_at
19
+ # @return [Time] The time the invite was created at.
20
+ # @see Invite#created_at
21
+ # @!attribute [r] max_age
22
+ # @return [Integer] The maximum age of the created invite.
23
+ # @see Invite#max_age
24
+ # @!attribute [r] max_uses
25
+ # @return [Integer] The maximum number of uses before the invite expires.
26
+ # @see Invite#max_uses
27
+ # @!attribute [r] temporary
28
+ # @return [true, false] Whether or not this invite grants temporary membership.
29
+ # @see Invite#temporary
30
+ # @!attribute [r] inviter
31
+ # @return [User] The user that created the invite.
32
+ # @see Invite#inviter
33
+ delegate :code, :created_at, :max_age, :max_uses, :temporary, :inviter, to: :invite
34
+
35
+ alias temporary? temporary
36
+
37
+ def initialize(data, invite, bot)
38
+ @bot = bot
39
+ @invite = invite
40
+ @channel = bot.channel(data['channel_id'])
41
+ @server = bot.server(data['guild_id']) if data['guild_id']
42
+ end
43
+ end
44
+
45
+ # Raised when an invite is deleted.
46
+ class InviteDeleteEvent < Event
47
+ # @return [Channel] The channel the deleted invite was for.
48
+ attr_reader :channel
49
+
50
+ # @return [Server, nil] The server the deleted invite was for.
51
+ attr_reader :server
52
+
53
+ # @return [String] The code of the deleted invite.
54
+ attr_reader :code
55
+
56
+ def initialize(data, bot)
57
+ @bot = bot
58
+ @channel = bot.channel(data['channel_id'])
59
+ @server = bot.server(data['guild_id']) if data['guild_id']
60
+ @code = data['code']
61
+ end
62
+ end
63
+
64
+ # Event handler for InviteCreateEvent.
65
+ class InviteCreateEventHandler < EventHandler
66
+ def matches?(event)
67
+ return false unless event.is_a? InviteCreateEvent
68
+
69
+ [
70
+ matches_all(@attributes[:server], event.server) do |a, e|
71
+ a == case a
72
+ when String
73
+ e.name
74
+ when Integer
75
+ e.id
76
+ else
77
+ e
78
+ end
79
+ end,
80
+ matches_all(@attributes[:channel], event.channel) do |a, e|
81
+ a == case a
82
+ when String
83
+ e.name
84
+ when Integer
85
+ e.id
86
+ else
87
+ e
88
+ end
89
+ end,
90
+ matches_all(@attributes[:temporary], event.temporary?, &:==),
91
+ matches_all(@attributes[:inviter], event.inviter, &:==)
92
+ ].reduce(true, &:&)
93
+ end
94
+ end
95
+
96
+ # Event handler for InviteDeleteEvent
97
+ class InviteDeleteEventHandler < EventHandler
98
+ def matches?(event)
99
+ return false unless event.is_a? InviteDeleteEvent
100
+
101
+ [
102
+ matches_all(@attributes[:server], event.server) do |a, e|
103
+ a == case a
104
+ when String
105
+ e.name
106
+ when Integer
107
+ e.id
108
+ else
109
+ e
110
+ end
111
+ end,
112
+ matches_all(@attributes[:channel], event.channel) do |a, e|
113
+ a == case a
114
+ when String
115
+ e.name
116
+ when Integer
117
+ e.id
118
+ else
119
+ e
120
+ end
121
+ end
122
+ ].reduce(true, &:&)
123
+ end
124
+ end
125
+ end
@@ -34,8 +34,9 @@ module Discordrb::Events
34
34
  end
35
35
 
36
36
  def init_roles(data, _)
37
- @roles = []
37
+ @roles = [@server.role(@server.id)]
38
38
  return unless data['roles']
39
+
39
40
  data['roles'].each do |element|
40
41
  role_id = element.to_i
41
42
  @roles << @server.roles.find { |r| r.id == role_id }
@@ -78,10 +79,13 @@ module Discordrb::Events
78
79
  # Member leaves
79
80
  # @see Discordrb::EventContainer#member_leave
80
81
  class ServerMemberDeleteEvent < ServerMemberEvent
81
- # Overide init_user to account for the deleted user on the server
82
+ # Override init_user to account for the deleted user on the server
82
83
  def init_user(data, bot)
83
84
  @user = Discordrb::User.new(data['user'], bot)
84
85
  end
86
+
87
+ # @return [User] the user in question.
88
+ attr_reader :user
85
89
  end
86
90
 
87
91
  # Event handler for {ServerMemberDeleteEvent}
@@ -14,27 +14,41 @@ module Discordrb::Events
14
14
  # @param content [String] The message to send to the channel
15
15
  # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
16
16
  # @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
17
+ # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
18
+ # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
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.
17
21
  # @return [Discordrb::Message] the message that was sent
18
- def send_message(content, tts = false, embed = nil)
19
- channel.send_message(content, tts, embed)
22
+ def send_message(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
23
+ channel.send_message(content, tts, embed, attachments, allowed_mentions, message_reference, components)
20
24
  end
21
25
 
22
26
  # The same as {#send_message}, but yields a {Webhooks::Embed} for easy building of embedded content inside a block.
23
27
  # @see Channel#send_embed
24
28
  # @param message [String] The message that should be sent along with the embed. If this is the empty string, only the embed will be shown.
25
29
  # @param embed [Discordrb::Webhooks::Embed, nil] The embed to start the building process with, or nil if one should be created anew.
30
+ # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
31
+ # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
32
+ # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
33
+ # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
34
+ # @param components [View, Array<Hash>, nil] A collection of components to attach to the message.
26
35
  # @yield [embed] Yields the embed to allow for easy building inside a block.
27
36
  # @yieldparam embed [Discordrb::Webhooks::Embed] The embed from the parameters, or a new one.
28
37
  # @return [Message] The resulting message.
29
- def send_embed(message = '', embed = nil, &block)
30
- channel.send_embed(message, embed, &block)
38
+ def send_embed(message = '', embed = nil, attachments = nil, tts = false, allowed_mentions = nil, message_reference = nil, components = nil, &block)
39
+ channel.send_embed(message, embed, attachments, tts, allowed_mentions, message_reference, components, &block)
31
40
  end
32
41
 
33
42
  # Sends a temporary message to the channel this message was sent in, right now.
34
43
  # @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
35
44
  # @param timeout [Float] The amount of time in seconds after which the message sent will be deleted.
36
- def send_temporary_message(content, timeout)
37
- channel.send_temporary_message(content, timeout)
45
+ # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
46
+ # @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
47
+ # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
48
+ # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
49
+ # @param components [View, Array<Hash>, nil] A collection of components to attach to the message.
50
+ def send_temporary_message(content, timeout, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, components = nil)
51
+ channel.send_temporary_message(content, timeout, tts, embed, attachments, allowed_mentions, components)
38
52
  end
39
53
 
40
54
  # Adds a string to be sent after the event has finished execution. Avoids problems with rate limiting because only
@@ -82,9 +96,15 @@ module Discordrb::Events
82
96
  # @return [String] the message that has been saved by calls to {#<<} and will be sent to Discord upon completion.
83
97
  attr_reader :saved_message
84
98
 
85
- # @return [File] the file that have been saved by calls to {#attach_file} and will be sent to Discord upon completion.
99
+ # @return [File] the file that has been saved by a call to {#attach_file} and will be sent to Discord upon completion.
86
100
  attr_reader :file
87
101
 
102
+ # @return [String] the filename set in {#attach_file} that will override the original filename when sent.
103
+ attr_reader :filename
104
+
105
+ # @return [true, false] Whether or not this file should appear as a spoiler. Set by {#attach_file}
106
+ attr_reader :file_spoiler
107
+
88
108
  # @!attribute [r] author
89
109
  # @return [Member, User] who sent this message.
90
110
  # @see Message#author
@@ -110,6 +130,8 @@ module Discordrb::Events
110
130
  @channel = message.channel
111
131
  @saved_message = ''
112
132
  @file = nil
133
+ @filename = nil
134
+ @file_spoiler = nil
113
135
  end
114
136
 
115
137
  # Sends file with a caption to the channel this message was sent in, right now.
@@ -117,25 +139,34 @@ module Discordrb::Events
117
139
  # because it avoids rate limiting problems
118
140
  # @param file [File] The file to send to the channel
119
141
  # @param caption [String] The caption attached to the file
142
+ # @param filename [String] Overrides the filename of the uploaded file
143
+ # @param spoiler [true, false] Whether or not this file should appear as a spoiler.
120
144
  # @return [Discordrb::Message] the message that was sent
121
145
  # @example Send a file from disk
122
146
  # event.send_file(File.open('rubytaco.png', 'r'))
123
- def send_file(file, caption: nil)
124
- @message.channel.send_file(file, caption: caption)
147
+ def send_file(file, caption: nil, filename: nil, spoiler: nil)
148
+ @message.channel.send_file(file, caption: caption, filename: filename, spoiler: spoiler)
125
149
  end
126
150
 
127
151
  # Attaches a file to the message event and converts the message into
128
152
  # a caption.
129
153
  # @param file [File] The file to be attached
130
- def attach_file(file)
154
+ # @param filename [String] Overrides the filename of the uploaded file
155
+ # @param spoiler [true, false] Whether or not this file should appear as a spoiler.
156
+ def attach_file(file, filename: nil, spoiler: nil)
131
157
  raise ArgumentError, 'Argument is not a file!' unless file.is_a?(File)
158
+
132
159
  @file = file
160
+ @filename = filename
161
+ @file_spoiler = spoiler
133
162
  nil
134
163
  end
135
164
 
136
165
  # Detaches a file from the message event.
137
166
  def detach_file
138
167
  @file = nil
168
+ @filename = nil
169
+ @file_spoiler = nil
139
170
  end
140
171
 
141
172
  # @return [true, false] whether or not this message was sent by the bot itself
@@ -161,51 +192,57 @@ module Discordrb::Events
161
192
 
162
193
  [
163
194
  matches_all(@attributes[:starting_with] || @attributes[:start_with], event.content) do |a, e|
164
- if a.is_a? String
195
+ case a
196
+ when String
165
197
  e.start_with? a
166
- elsif a.is_a? Regexp
167
- (e =~ a) && (e =~ a).zero?
198
+ when Regexp
199
+ (e =~ a)&.zero?
168
200
  end
169
201
  end,
170
202
  matches_all(@attributes[:ending_with] || @attributes[:end_with], event.content) do |a, e|
171
- if a.is_a? String
203
+ case a
204
+ when String
172
205
  e.end_with? a
173
- elsif a.is_a? Regexp
206
+ when Regexp
174
207
  !(e =~ Regexp.new("#{a}$")).nil?
175
208
  end
176
209
  end,
177
210
  matches_all(@attributes[:containing] || @attributes[:contains], event.content) do |a, e|
178
- if a.is_a? String
211
+ case a
212
+ when String
179
213
  e.include? a
180
- elsif a.is_a? Regexp
214
+ when Regexp
181
215
  (e =~ a)
182
216
  end
183
217
  end,
184
218
  matches_all(@attributes[:in], event.channel) do |a, e|
185
- if a.is_a? String
219
+ case a
220
+ when String
186
221
  # Make sure to remove the "#" from channel names in case it was specified
187
222
  a.delete('#') == e.name
188
- elsif a.is_a? Integer
223
+ when Integer
189
224
  a == e.id
190
225
  else
191
226
  a == e
192
227
  end
193
228
  end,
194
229
  matches_all(@attributes[:from], event.author) do |a, e|
195
- if a.is_a? String
230
+ case a
231
+ when String
196
232
  a == e.name
197
- elsif a.is_a? Integer
233
+ when Integer
198
234
  a == e.id
199
- elsif a == :bot
235
+ when :bot
200
236
  e.current_bot?
201
237
  else
202
238
  a == e
203
239
  end
204
240
  end,
205
241
  matches_all(@attributes[:with_text] || @attributes[:content] || @attributes[:exact_text], event.content) do |a, e|
206
- if a.is_a? String
242
+ case a
243
+ when String
207
244
  e == a
208
- elsif a.is_a? Regexp
245
+ when Regexp
209
246
  match = a.match(e)
210
247
  match ? (e == match[0]) : false
211
248
  end
@@ -221,7 +258,7 @@ module Discordrb::Events
221
258
  if event.file.nil?
222
259
  event.send_message(event.saved_message) unless event.saved_message.empty?
223
260
  else
224
- event.send_file(event.file, caption: event.saved_message)
261
+ event.send_file(event.file, caption: event.saved_message, filename: event.filename, spoiler: event.file_spoiler)
225
262
  end
226
263
  end
227
264
  end
@@ -265,10 +302,11 @@ module Discordrb::Events
265
302
  a.resolve_id == e.resolve_id
266
303
  end,
267
304
  matches_all(@attributes[:in], event.channel) do |a, e|
268
- if a.is_a? String
305
+ case a
306
+ when String
269
307
  # Make sure to remove the "#" from channel names in case it was specified
270
308
  a.delete('#') == e.name
271
- elsif a.is_a? Integer
309
+ when Integer
272
310
  a == e.id
273
311
  else
274
312
  a == e
@@ -291,4 +329,11 @@ module Discordrb::Events
291
329
 
292
330
  # Event handler for {MessageDeleteEvent}
293
331
  class MessageDeleteEventHandler < MessageIDEventHandler; end
332
+
333
+ # Raised whenever a MESSAGE_UPDATE is received
334
+ # @see Discordrb::EventContainer#message_update
335
+ class MessageUpdateEvent < MessageEvent; end
336
+
337
+ # Event handler for {MessageUpdateEvent}
338
+ class MessageUpdateEventHandler < MessageEventHandler; end
294
339
  end
@@ -15,11 +15,16 @@ module Discordrb::Events
15
15
  # @return [Symbol] the new status.
16
16
  attr_reader :status
17
17
 
18
+ # @return [Hash<Symbol, Symbol>] the current online status (`:online`, `:idle` or `:dnd`) of the user
19
+ # on various device types (`:desktop`, `:mobile`, or `:web`). The value will be `nil` if the user is offline or invisible.
20
+ attr_reader :client_status
21
+
18
22
  def initialize(data, bot)
19
23
  @bot = bot
20
24
 
21
25
  @user = bot.user(data['user']['id'].to_i)
22
26
  @status = data['status'].to_sym
27
+ @client_status = user.client_status
23
28
  @server = bot.server(data['guild_id'].to_i)
24
29
  end
25
30
  end
@@ -32,9 +37,10 @@ module Discordrb::Events
32
37
 
33
38
  [
34
39
  matches_all(@attributes[:from], event.user) do |a, e|
35
- a == if a.is_a? String
40
+ a == case a
41
+ when String
36
42
  e.name
37
- elsif a.is_a? Integer
43
+ when Integer
38
44
  e.id
39
45
  else
40
46
  e
@@ -59,28 +65,35 @@ module Discordrb::Events
59
65
  # @return [User] the user whose status got updated.
60
66
  attr_reader :user
61
67
 
62
- # @return [String] the new game the user is playing.
63
- attr_reader :game
68
+ # @return [Discordrb::Activity] The new activity
69
+ attr_reader :activity
64
70
 
65
- # @return [String] the URL to the stream
66
- attr_reader :url
71
+ # @!attribute [r] url
72
+ # @return [String] the URL to the stream
67
73
 
68
- # @return [String] what the player is currently doing (ex. game being streamed)
69
- attr_reader :details
74
+ # @!attribute [r] details
75
+ # @return [String] what the player is currently doing (ex. game being streamed)
70
76
 
71
- # @return [Integer] the type of play. 0 = game, 1 = Twitch
72
- attr_reader :type
77
+ # @!attribute [r] type
78
+ # @return [Integer] the type of play. See {Discordrb::Activity}
79
+ delegate :url, :details, :type, to: :activity
73
80
 
74
- def initialize(data, bot)
81
+ # @return [Hash<Symbol, Symbol>] the current online status (`:online`, `:idle` or `:dnd`) of the user
82
+ # on various device types (`:desktop`, `:mobile`, or `:web`). The value will be `nil` if the user is offline or invisible.
83
+ attr_reader :client_status
84
+
85
+ def initialize(data, activity, bot)
75
86
  @bot = bot
87
+ @activity = activity
76
88
 
77
89
  @server = bot.server(data['guild_id'].to_i)
78
90
  @user = bot.user(data['user']['id'].to_i)
79
- @game = data['game'] ? data['game']['name'] : nil
80
- @type = data['game'] ? data['game']['type'].to_i : nil
81
- # Handle optional 'game' fields safely
82
- @url = data['game'] && data['game']['url'] ? data['game']['url'] : nil
83
- @details = data['game'] && data['game']['details'] ? data['game']['details'] : nil
91
+ @client_status = @user.client_status
92
+ end
93
+
94
+ # @return [String] the name of the new game the user is playing.
95
+ def game
96
+ @activity.name
84
97
  end
85
98
  end
86
99
 
@@ -92,9 +105,10 @@ module Discordrb::Events
92
105
 
93
106
  [
94
107
  matches_all(@attributes[:from], event.user) do |a, e|
95
- a == if a.is_a? String
108
+ a == case a
109
+ when String
96
110
  e.name
97
- elsif a.is_a? Integer
111
+ when Integer
98
112
  e.id
99
113
  else
100
114
  e
@@ -105,6 +119,9 @@ module Discordrb::Events
105
119
  end,
106
120
  matches_all(@attributes[:type], event.type) do |a, e|
107
121
  a == e
122
+ end,
123
+ matches_all(@attributes[:client_status], event.client_status) do |a, e|
124
+ e.slice(a.keys) == a
108
125
  end
109
126
  ].reduce(true, &:&)
110
127
  end
@@ -30,9 +30,7 @@ module Discordrb::Events
30
30
  [
31
31
  matches_all(@attributes[:type] || @attributes[:t], event.type) do |a, e|
32
32
  if a.is_a? Regexp
33
- # 24: update to matches?
34
- match = a.match(e)
35
- match ? (e == match[0]) : false
33
+ a.match?(e)
36
34
  else
37
35
  e.to_s.casecmp(a.to_s).zero?
38
36
  end
@@ -11,6 +11,9 @@ module Discordrb::Events
11
11
  # @return [Emoji] the emoji that was reacted with.
12
12
  attr_reader :emoji
13
13
 
14
+ # @!visibility private
15
+ attr_reader :message_id
16
+
14
17
  def initialize(data, bot)
15
18
  @bot = bot
16
19
 
@@ -54,13 +57,38 @@ module Discordrb::Events
54
57
 
55
58
  [
56
59
  matches_all(@attributes[:emoji], event.emoji) do |a, e|
57
- if a.is_a? Integer
60
+ case a
61
+ when Integer
58
62
  e.id == a
59
- elsif a.is_a? String
63
+ when String
60
64
  e.name == a || e.name == a.delete(':') || e.id == a.resolve_id
61
65
  else
62
66
  e == a
63
67
  end
68
+ end,
69
+ matches_all(@attributes[:message], event.message_id) do |a, e|
70
+ a == e
71
+ end,
72
+ matches_all(@attributes[:in], event.channel) do |a, e|
73
+ case a
74
+ when String
75
+ # Make sure to remove the "#" from channel names in case it was specified
76
+ a.delete('#') == e.name
77
+ when Integer
78
+ a == e.id
79
+ else
80
+ a == e
81
+ end
82
+ end,
83
+ matches_all(@attributes[:from], event.user) do |a, e|
84
+ case a
85
+ when String
86
+ a == e.name
87
+ when :bot
88
+ e.current_bot?
89
+ else
90
+ a == e
91
+ end
64
92
  end
65
93
  ].reduce(true, &:&)
66
94
  end
@@ -82,6 +110,9 @@ module Discordrb::Events
82
110
  class ReactionRemoveAllEvent < Event
83
111
  include Respondable
84
112
 
113
+ # @!visibility private
114
+ attr_reader :message_id
115
+
85
116
  def initialize(data, bot)
86
117
  @bot = bot
87
118
 
@@ -106,8 +137,22 @@ module Discordrb::Events
106
137
  # Check for the proper event type
107
138
  return false unless event.is_a? ReactionRemoveAllEvent
108
139
 
109
- # No attributes yet as there is no property available on the event that doesn't involve doing a resolution request
110
- [].reduce(true, &:&)
140
+ [
141
+ matches_all(@attributes[:message], event.message_id) do |a, e|
142
+ a == e
143
+ end,
144
+ matches_all(@attributes[:in], event.channel) do |a, e|
145
+ case a
146
+ when String
147
+ # Make sure to remove the "#" from channel names in case it was specified
148
+ a.delete('#') == e.name
149
+ when Integer
150
+ a == e.id
151
+ else
152
+ a == e
153
+ end
154
+ end
155
+ ].reduce(true, &:&)
111
156
  end
112
157
  end
113
158
  end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb::Events
4
+ # Raised when a thread is created
5
+ class ThreadCreateEvent < Event
6
+ # @return [Channel] the thread in question.
7
+ attr_reader :thread
8
+
9
+ delegate :name, :server, :owner, :parent_channel, :thread_metadata, to: :thread
10
+
11
+ def initialize(data, bot)
12
+ @bot = bot
13
+ @thread = data.is_a?(Discordrb::Channel) ? data : bot.channel(data['id'].to_i)
14
+ end
15
+ end
16
+
17
+ # Event handler for ChannelCreateEvent
18
+ class ThreadCreateEventHandler < EventHandler
19
+ def matches?(event)
20
+ # Check for the proper event type
21
+ return false unless event.is_a? ThreadCreateEvent
22
+
23
+ [
24
+ matches_all(@attributes[:name], event.name) do |a, e|
25
+ a == if a.is_a? String
26
+ e.to_s
27
+ else
28
+ e
29
+ end
30
+ end,
31
+ matches_all(@attributes[:server], event.server) do |a, e|
32
+ a.resolve_id == e.resolve_id
33
+ end,
34
+ matches_all(@attributes[:invitable], event.thread.invitable) do |a, e|
35
+ a == e
36
+ end,
37
+ matches_all(@attributes[:owner], event.thread.owner) do |a, e|
38
+ a.resolve_id == e.resolve_id
39
+ end,
40
+ matches_all(@attributes[:channel], event.thread.parent) do |a, e|
41
+ a.resolve_id == e.resolve_id
42
+ end
43
+ ].reduce(true, &:&)
44
+ end
45
+ end
46
+
47
+ # Raised when a thread is updated (e.g. name changes)
48
+ class ThreadUpdateEvent < ThreadCreateEvent; end
49
+
50
+ # Event handler for ThreadUpdateEvent
51
+ class ThreadUpdateEventHandler < ThreadCreateEventHandler
52
+ def matches?(event)
53
+ # Check for the proper event type
54
+ return false unless event.is_a? ThreadUpdateEvent
55
+
56
+ super
57
+ end
58
+ end
59
+
60
+ # Raised when members are added or removed from a thread.
61
+ class ThreadMembersUpdateEvent < Event
62
+ # @return [Channel]
63
+ attr_reader :thread
64
+
65
+ # @return [Array<Member, User>]
66
+ attr_reader :added_members
67
+
68
+ # @return [Array<Integer>]
69
+ attr_reader :removed_member_ids
70
+
71
+ # @return [Integer]
72
+ attr_reader :member_count
73
+
74
+ delegate :name, :server, :owner, :parent_channel, :thread_metadata, to: :thread
75
+
76
+ def initialize(data, bot)
77
+ @bot = bot
78
+ @thread = data.is_a?(Discordrb::Channel) ? data : bot.channel(data['id'].to_i)
79
+ @added_members = data['added_members']&.map do |member|
80
+ data['guild_id'] ? bot.member(data['guild_id'], member['user_id']) : bot.user(member['user_id'])
81
+ end || []
82
+ @removed_member_ids = data['removed_member_ids']&.map(&:resolve_id) || []
83
+ @member_count = data['member_count']
84
+ end
85
+ end
86
+
87
+ # Event handler for ThreadMembersUpdateEvent
88
+ class ThreadMembersUpdateEventHandler < ThreadCreateEventHandler
89
+ def matches?(event)
90
+ # Check for the proper event type
91
+ return false unless event.is_a? ThreadMembersUpdateEvent
92
+
93
+ super
94
+ end
95
+ end
96
+ end
@@ -45,18 +45,20 @@ module Discordrb::Events
45
45
 
46
46
  [
47
47
  matches_all(@attributes[:in], event.channel) do |a, e|
48
- if a.is_a? String
48
+ case a
49
+ when String
49
50
  a.delete('#') == e.name
50
- elsif a.is_a? Integer
51
+ when Integer
51
52
  a == e.id
52
53
  else
53
54
  a == e
54
55
  end
55
56
  end,
56
57
  matches_all(@attributes[:from], event.user) do |a, e|
57
- a == if a.is_a? String
58
+ a == case a
59
+ when String
58
60
  e.name
59
- elsif a.is_a? Integer
61
+ when Integer
60
62
  e.id
61
63
  else
62
64
  e