discordrb 3.3.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
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