rubycord 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/lib/rubycord/allowed_mentions.rb +34 -0
  3. data/lib/rubycord/api/application.rb +200 -0
  4. data/lib/rubycord/api/channel.rb +597 -0
  5. data/lib/rubycord/api/interaction.rb +52 -0
  6. data/lib/rubycord/api/invite.rb +42 -0
  7. data/lib/rubycord/api/server.rb +557 -0
  8. data/lib/rubycord/api/user.rb +153 -0
  9. data/lib/rubycord/api/webhook.rb +138 -0
  10. data/lib/rubycord/api.rb +356 -0
  11. data/lib/rubycord/await.rb +49 -0
  12. data/lib/rubycord/bot.rb +1757 -0
  13. data/lib/rubycord/cache.rb +259 -0
  14. data/lib/rubycord/colour_rgb.rb +41 -0
  15. data/lib/rubycord/commands/command_bot.rb +519 -0
  16. data/lib/rubycord/commands/container.rb +110 -0
  17. data/lib/rubycord/commands/events.rb +9 -0
  18. data/lib/rubycord/commands/parser.rb +325 -0
  19. data/lib/rubycord/commands/rate_limiter.rb +142 -0
  20. data/lib/rubycord/container.rb +753 -0
  21. data/lib/rubycord/data/activity.rb +269 -0
  22. data/lib/rubycord/data/application.rb +48 -0
  23. data/lib/rubycord/data/attachment.rb +109 -0
  24. data/lib/rubycord/data/audit_logs.rb +343 -0
  25. data/lib/rubycord/data/channel.rb +996 -0
  26. data/lib/rubycord/data/component.rb +227 -0
  27. data/lib/rubycord/data/embed.rb +249 -0
  28. data/lib/rubycord/data/emoji.rb +80 -0
  29. data/lib/rubycord/data/integration.rb +120 -0
  30. data/lib/rubycord/data/interaction.rb +798 -0
  31. data/lib/rubycord/data/invite.rb +135 -0
  32. data/lib/rubycord/data/member.rb +370 -0
  33. data/lib/rubycord/data/message.rb +412 -0
  34. data/lib/rubycord/data/overwrite.rb +106 -0
  35. data/lib/rubycord/data/profile.rb +89 -0
  36. data/lib/rubycord/data/reaction.rb +31 -0
  37. data/lib/rubycord/data/recipient.rb +32 -0
  38. data/lib/rubycord/data/role.rb +246 -0
  39. data/lib/rubycord/data/server.rb +1002 -0
  40. data/lib/rubycord/data/user.rb +261 -0
  41. data/lib/rubycord/data/voice_region.rb +43 -0
  42. data/lib/rubycord/data/voice_state.rb +39 -0
  43. data/lib/rubycord/data/webhook.rb +232 -0
  44. data/lib/rubycord/data.rb +40 -0
  45. data/lib/rubycord/errors.rb +737 -0
  46. data/lib/rubycord/events/await.rb +46 -0
  47. data/lib/rubycord/events/bans.rb +58 -0
  48. data/lib/rubycord/events/channels.rb +186 -0
  49. data/lib/rubycord/events/generic.rb +126 -0
  50. data/lib/rubycord/events/guilds.rb +191 -0
  51. data/lib/rubycord/events/interactions.rb +480 -0
  52. data/lib/rubycord/events/invites.rb +123 -0
  53. data/lib/rubycord/events/lifetime.rb +29 -0
  54. data/lib/rubycord/events/members.rb +91 -0
  55. data/lib/rubycord/events/message.rb +337 -0
  56. data/lib/rubycord/events/presence.rb +127 -0
  57. data/lib/rubycord/events/raw.rb +45 -0
  58. data/lib/rubycord/events/reactions.rb +156 -0
  59. data/lib/rubycord/events/roles.rb +86 -0
  60. data/lib/rubycord/events/threads.rb +94 -0
  61. data/lib/rubycord/events/typing.rb +70 -0
  62. data/lib/rubycord/events/voice_server_update.rb +45 -0
  63. data/lib/rubycord/events/voice_state_update.rb +103 -0
  64. data/lib/rubycord/events/webhooks.rb +62 -0
  65. data/lib/rubycord/gateway.rb +867 -0
  66. data/lib/rubycord/id_object.rb +37 -0
  67. data/lib/rubycord/light/data.rb +60 -0
  68. data/lib/rubycord/light/integrations.rb +71 -0
  69. data/lib/rubycord/light/light_bot.rb +56 -0
  70. data/lib/rubycord/light.rb +6 -0
  71. data/lib/rubycord/logger.rb +118 -0
  72. data/lib/rubycord/paginator.rb +55 -0
  73. data/lib/rubycord/permissions.rb +251 -0
  74. data/lib/rubycord/version.rb +5 -0
  75. data/lib/rubycord/voice/encoder.rb +113 -0
  76. data/lib/rubycord/voice/network.rb +366 -0
  77. data/lib/rubycord/voice/sodium.rb +96 -0
  78. data/lib/rubycord/voice/voice_bot.rb +408 -0
  79. data/lib/rubycord/webhooks/builder.rb +100 -0
  80. data/lib/rubycord/webhooks/client.rb +132 -0
  81. data/lib/rubycord/webhooks/embeds.rb +248 -0
  82. data/lib/rubycord/webhooks/modal.rb +78 -0
  83. data/lib/rubycord/webhooks/version.rb +7 -0
  84. data/lib/rubycord/webhooks/view.rb +192 -0
  85. data/lib/rubycord/webhooks.rb +12 -0
  86. data/lib/rubycord/websocket.rb +70 -0
  87. data/lib/rubycord.rb +140 -0
  88. metadata +231 -0
@@ -0,0 +1,91 @@
1
+ require "rubycord/events/generic"
2
+ require "rubycord/data"
3
+
4
+ module Rubycord::Events
5
+ # Generic subclass for server member events (add/update/delete)
6
+ class ServerMemberEvent < Event
7
+ # @return [Member] the member in question.
8
+ attr_reader :user
9
+ alias_method :member, :user
10
+
11
+ # @return [Array<Role>] the member's roles.
12
+ attr_reader :roles
13
+
14
+ # @return [Server] the server on which the event happened.
15
+ attr_reader :server
16
+
17
+ def initialize(data, bot)
18
+ @bot = bot
19
+
20
+ @server = bot.server(data["guild_id"].to_i)
21
+ return unless @server
22
+
23
+ init_user(data, bot)
24
+ init_roles(data, bot)
25
+ end
26
+
27
+ private
28
+
29
+ def init_user(data, _)
30
+ user_id = data["user"]["id"].to_i
31
+ @user = @server.member(user_id)
32
+ end
33
+
34
+ def init_roles(data, _)
35
+ @roles = [@server.role(@server.id)]
36
+ return unless data["roles"]
37
+
38
+ data["roles"].each do |element|
39
+ role_id = element.to_i
40
+ @roles << @server.roles.find { |r| r.id == role_id }
41
+ end
42
+ end
43
+ end
44
+
45
+ # Generic event handler for member events
46
+ class ServerMemberEventHandler < EventHandler
47
+ def matches?(event)
48
+ # Check for the proper event type
49
+ return false unless event.is_a? ServerMemberEvent
50
+
51
+ [
52
+ matches_all(@attributes[:username], event.user.name) do |a, e|
53
+ a == if a.is_a? String
54
+ e.to_s
55
+ else
56
+ e
57
+ end
58
+ end
59
+ ].reduce(true, &:&)
60
+ end
61
+ end
62
+
63
+ # Member joins
64
+ # @see Rubycord::EventContainer#member_join
65
+ class ServerMemberAddEvent < ServerMemberEvent; end
66
+
67
+ # Event handler for {ServerMemberAddEvent}
68
+ class ServerMemberAddEventHandler < ServerMemberEventHandler; end
69
+
70
+ # Member is updated (roles added or deleted)
71
+ # @see Rubycord::EventContainer#member_update
72
+ class ServerMemberUpdateEvent < ServerMemberEvent; end
73
+
74
+ # Event handler for {ServerMemberUpdateEvent}
75
+ class ServerMemberUpdateEventHandler < ServerMemberEventHandler; end
76
+
77
+ # Member leaves
78
+ # @see Rubycord::EventContainer#member_leave
79
+ class ServerMemberDeleteEvent < ServerMemberEvent
80
+ # Override init_user to account for the deleted user on the server
81
+ def init_user(data, bot)
82
+ @user = Rubycord::User.new(data["user"], bot)
83
+ end
84
+
85
+ # @return [User] the user in question.
86
+ attr_reader :user
87
+ end
88
+
89
+ # Event handler for {ServerMemberDeleteEvent}
90
+ class ServerMemberDeleteEventHandler < ServerMemberEventHandler; end
91
+ end
@@ -0,0 +1,337 @@
1
+ require "rubycord/events/generic"
2
+ require "rubycord/data"
3
+
4
+ module Rubycord::Events
5
+ # Module to make sending messages easier with the presence of a text channel in an event
6
+ module Respondable
7
+ # @return [Channel] the channel in which this event occurred
8
+ attr_reader :channel
9
+
10
+ # Sends a message to the channel this message was sent in, right now. It is usually preferable to use {#<<} instead
11
+ # because it avoids rate limiting problems
12
+ # @param content [String] The message to send to the channel
13
+ # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
14
+ # @param embed [Hash, Rubycord::Webhooks::Embed, nil] The rich embed to append to this message.
15
+ # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
16
+ # @param allowed_mentions [Hash, Rubycord::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
17
+ # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
18
+ # @param components [View, Array<Hash>, nil] A collection of components to attach to the message.
19
+ # @return [Rubycord::Message] the message that was sent
20
+ def send_message(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
21
+ channel.send_message(content, tts, embed, attachments, allowed_mentions, message_reference, components)
22
+ end
23
+
24
+ # The same as {#send_message}, but yields a {Webhooks::Embed} for easy building of embedded content inside a block.
25
+ # @see Channel#send_embed
26
+ # @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.
27
+ # @param embed [Rubycord::Webhooks::Embed, nil] The embed to start the building process with, or nil if one should be created anew.
28
+ # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
29
+ # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
30
+ # @param allowed_mentions [Hash, Rubycord::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
31
+ # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
32
+ # @param components [View, Array<Hash>, nil] A collection of components to attach to the message.
33
+ # @yield [embed] Yields the embed to allow for easy building inside a block.
34
+ # @yieldparam embed [Rubycord::Webhooks::Embed] The embed from the parameters, or a new one.
35
+ # @return [Message] The resulting message.
36
+ def send_embed(message = "", embed = nil, attachments = nil, tts = false, allowed_mentions = nil, message_reference = nil, components = nil, &)
37
+ channel.send_embed(message, embed, attachments, tts, allowed_mentions, message_reference, components, &)
38
+ end
39
+
40
+ # Sends a temporary message to the channel this message was sent in, right now.
41
+ # @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
42
+ # @param timeout [Float] The amount of time in seconds after which the message sent will be deleted.
43
+ # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
44
+ # @param embed [Hash, Rubycord::Webhooks::Embed, nil] The rich embed to append to this message.
45
+ # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
46
+ # @param allowed_mentions [Hash, Rubycord::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
47
+ # @param components [View, Array<Hash>, nil] A collection of components to attach to the message.
48
+ def send_temporary_message(content, timeout, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, components = nil)
49
+ channel.send_temporary_message(content, timeout, tts, embed, attachments, allowed_mentions, components)
50
+ end
51
+
52
+ # Adds a string to be sent after the event has finished execution. Avoids problems with rate limiting because only
53
+ # one message is ever sent. If it is used multiple times, the strings will bunch up into one message (separated by
54
+ # newlines)
55
+ # @param message [String] The message to send to the channel
56
+ def <<(message)
57
+ addition = "#{message}\n"
58
+ @saved_message = @saved_message ? @saved_message + addition : addition
59
+ nil
60
+ end
61
+
62
+ # Drains the currently saved message, which clears it out, resulting in everything being saved before being
63
+ # thrown away and nothing being sent to the channel (unless there is something saved after this).
64
+ # @see #<<
65
+ def drain
66
+ @saved_message = ""
67
+ nil
68
+ end
69
+
70
+ # Drains the currently saved message into a result string. This prepends it before that string, clears the saved
71
+ # message and returns the concatenation.
72
+ # @param result [String] The result string to drain into.
73
+ # @return [String] a string formed by concatenating the saved message and the argument.
74
+ def drain_into(result)
75
+ return if result.is_a?(Rubycord::Message)
76
+
77
+ result = (@saved_message.nil? ? "" : @saved_message.to_s) + (result.nil? ? "" : result.to_s)
78
+ drain
79
+ result
80
+ end
81
+
82
+ alias_method :send, :send_message
83
+ alias_method :respond, :send_message
84
+ alias_method :send_temp, :send_temporary_message
85
+ end
86
+
87
+ # Event raised when a text message is sent to a channel
88
+ class MessageEvent < Event
89
+ include Respondable
90
+
91
+ # @return [Message] the message which triggered this event.
92
+ attr_reader :message
93
+
94
+ # @return [String] the message that has been saved by calls to {#<<} and will be sent to Discord upon completion.
95
+ attr_reader :saved_message
96
+
97
+ # @return [File] the file that has been saved by a call to {#attach_file} and will be sent to Discord upon completion.
98
+ attr_reader :file
99
+
100
+ # @return [String] the filename set in {#attach_file} that will override the original filename when sent.
101
+ attr_reader :filename
102
+
103
+ # @return [true, false] Whether or not this file should appear as a spoiler. Set by {#attach_file}
104
+ attr_reader :file_spoiler
105
+
106
+ # @!attribute [r] author
107
+ # @return [Member, User] who sent this message.
108
+ # @see Message#author
109
+ # @!attribute [r] channel
110
+ # @return [Channel] the channel in which this message was sent.
111
+ # @see Message#channel
112
+ # @!attribute [r] content
113
+ # @return [String] the message's content.
114
+ # @see Message#content
115
+ # @!attribute [r] timestamp
116
+ # @return [Time] the time at which the message was sent.
117
+ # @see Message#timestamp
118
+ delegate :author, :channel, :content, :timestamp, to: :message
119
+
120
+ # @!attribute [r] server
121
+ # @return [Server, nil] the server where this message was sent, or nil if it was sent in PM.
122
+ # @see Channel#server
123
+ delegate :server, to: :channel
124
+
125
+ def initialize(message, bot)
126
+ @bot = bot
127
+ @message = message
128
+ @channel = message.channel
129
+ @saved_message = ""
130
+ @file = nil
131
+ @filename = nil
132
+ @file_spoiler = nil
133
+ end
134
+
135
+ # Sends file with a caption to the channel this message was sent in, right now.
136
+ # It is usually preferable to use {#<<} and {#attach_file} instead
137
+ # because it avoids rate limiting problems
138
+ # @param file [File] The file to send to the channel
139
+ # @param caption [String] The caption attached to the file
140
+ # @param filename [String] Overrides the filename of the uploaded file
141
+ # @param spoiler [true, false] Whether or not this file should appear as a spoiler.
142
+ # @return [Rubycord::Message] the message that was sent
143
+ # @example Send a file from disk
144
+ # event.send_file(File.open('rubytaco.png', 'r'))
145
+ def send_file(file, caption: nil, filename: nil, spoiler: nil)
146
+ @message.channel.send_file(file, caption: caption, filename: filename, spoiler: spoiler)
147
+ end
148
+
149
+ # Attaches a file to the message event and converts the message into
150
+ # a caption.
151
+ # @param file [File] The file to be attached
152
+ # @param filename [String] Overrides the filename of the uploaded file
153
+ # @param spoiler [true, false] Whether or not this file should appear as a spoiler.
154
+ def attach_file(file, filename: nil, spoiler: nil)
155
+ raise ArgumentError, "Argument is not a file!" unless file.is_a?(File)
156
+
157
+ @file = file
158
+ @filename = filename
159
+ @file_spoiler = spoiler
160
+ nil
161
+ end
162
+
163
+ # Detaches a file from the message event.
164
+ def detach_file
165
+ @file = nil
166
+ @filename = nil
167
+ @file_spoiler = nil
168
+ end
169
+
170
+ # @return [true, false] whether or not this message was sent by the bot itself
171
+ def from_bot?
172
+ @message.user.id == @bot.profile.id
173
+ end
174
+
175
+ # Utility method to get the voice bot for the current server
176
+ # @return [VoiceBot, nil] the voice bot connected to this message's server, or nil if there is none connected
177
+ def voice
178
+ @bot.voice(@message.channel.server.id)
179
+ end
180
+
181
+ alias_method :user, :author
182
+ alias_method :text, :content
183
+ end
184
+
185
+ # Event handler for MessageEvent
186
+ class MessageEventHandler < EventHandler
187
+ def matches?(event)
188
+ # Check for the proper event type
189
+ return false unless event.is_a? MessageEvent
190
+
191
+ [
192
+ matches_all(@attributes[:starting_with] || @attributes[:start_with], event.content) do |a, e|
193
+ case a
194
+ when String
195
+ e.start_with? a
196
+ when Regexp
197
+ (e =~ a)&.zero?
198
+ end
199
+ end,
200
+ matches_all(@attributes[:ending_with] || @attributes[:end_with], event.content) do |a, e|
201
+ case a
202
+ when String
203
+ e.end_with? a
204
+ when Regexp
205
+ !(e =~ Regexp.new("#{a}$")).nil?
206
+ end
207
+ end,
208
+ matches_all(@attributes[:containing] || @attributes[:contains], event.content) do |a, e|
209
+ case a
210
+ when String
211
+ e.include? a
212
+ when Regexp
213
+ (e =~ a)
214
+ end
215
+ end,
216
+ matches_all(@attributes[:in], event.channel) do |a, e|
217
+ case a
218
+ when String
219
+ # Make sure to remove the "#" from channel names in case it was specified
220
+ a.delete("#") == e.name
221
+ when Integer
222
+ a == e.id
223
+ else
224
+ a == e
225
+ end
226
+ end,
227
+ matches_all(@attributes[:from], event.author) do |a, e|
228
+ case a
229
+ when String
230
+ a == e.name
231
+ when Integer
232
+ a == e.id
233
+ when :bot
234
+ e.current_bot?
235
+ else
236
+ a == e
237
+ end
238
+ end,
239
+ matches_all(@attributes[:with_text] || @attributes[:content] || @attributes[:exact_text], event.content) do |a, e|
240
+ case a
241
+ when String
242
+ e == a
243
+ when Regexp
244
+ match = a.match(e)
245
+ match ? (e == match[0]) : false
246
+ end
247
+ end,
248
+ matches_all(@attributes[:after], event.timestamp) { |a, e| a > e },
249
+ matches_all(@attributes[:before], event.timestamp) { |a, e| a < e },
250
+ matches_all(@attributes[:private], event.channel.private?) { |a, e| !e == !a }
251
+ ].reduce(true, &:&)
252
+ end
253
+
254
+ # @see EventHandler#after_call
255
+ def after_call(event)
256
+ if event.file.nil?
257
+ event.send_message(event.saved_message) unless event.saved_message.empty?
258
+ else
259
+ event.send_file(event.file, caption: event.saved_message, filename: event.filename, spoiler: event.file_spoiler)
260
+ end
261
+ end
262
+ end
263
+
264
+ # @see Rubycord::EventContainer#mention
265
+ class MentionEvent < MessageEvent; end
266
+
267
+ # Event handler for {MentionEvent}
268
+ class MentionEventHandler < MessageEventHandler; end
269
+
270
+ # @see Rubycord::EventContainer#pm
271
+ class PrivateMessageEvent < MessageEvent; end
272
+
273
+ # Event handler for {PrivateMessageEvent}
274
+ class PrivateMessageEventHandler < MessageEventHandler; end
275
+
276
+ # A subset of MessageEvent that only contains a message ID and a channel
277
+ class MessageIDEvent < Event
278
+ include Respondable
279
+
280
+ # @return [Integer] the ID associated with this event
281
+ attr_reader :id
282
+
283
+ # @!visibility private
284
+ def initialize(data, bot)
285
+ @id = data["id"].to_i
286
+ @channel = bot.channel(data["channel_id"].to_i)
287
+ @saved_message = ""
288
+ @bot = bot
289
+ end
290
+ end
291
+
292
+ # Event handler for {MessageIDEvent}
293
+ class MessageIDEventHandler < EventHandler
294
+ def matches?(event)
295
+ # Check for the proper event type
296
+ return false unless event.is_a? MessageIDEvent
297
+
298
+ [
299
+ matches_all(@attributes[:id], event.id) do |a, e|
300
+ a.resolve_id == e.resolve_id
301
+ end,
302
+ matches_all(@attributes[:in], event.channel) do |a, e|
303
+ case a
304
+ when String
305
+ # Make sure to remove the "#" from channel names in case it was specified
306
+ a.delete("#") == e.name
307
+ when Integer
308
+ a == e.id
309
+ else
310
+ a == e
311
+ end
312
+ end
313
+ ].reduce(true, &:&)
314
+ end
315
+ end
316
+
317
+ # Raised when a message is edited
318
+ # @see Rubycord::EventContainer#message_edit
319
+ class MessageEditEvent < MessageEvent; end
320
+
321
+ # Event handler for {MessageEditEvent}
322
+ class MessageEditEventHandler < MessageEventHandler; end
323
+
324
+ # Raised when a message is deleted
325
+ # @see Rubycord::EventContainer#message_delete
326
+ class MessageDeleteEvent < MessageIDEvent; end
327
+
328
+ # Event handler for {MessageDeleteEvent}
329
+ class MessageDeleteEventHandler < MessageIDEventHandler; end
330
+
331
+ # Raised whenever a MESSAGE_UPDATE is received
332
+ # @see Rubycord::EventContainer#message_update
333
+ class MessageUpdateEvent < MessageEvent; end
334
+
335
+ # Event handler for {MessageUpdateEvent}
336
+ class MessageUpdateEventHandler < MessageEventHandler; end
337
+ end
@@ -0,0 +1,127 @@
1
+ require "rubycord/events/generic"
2
+ require "rubycord/data"
3
+
4
+ module Rubycord::Events
5
+ # Event raised when a user's presence state updates (idle or online)
6
+ class PresenceEvent < Event
7
+ # @return [Server] the server on which the presence update happened.
8
+ attr_reader :server
9
+
10
+ # @return [User] the user whose status got updated.
11
+ attr_reader :user
12
+
13
+ # @return [Symbol] the new status.
14
+ attr_reader :status
15
+
16
+ # @return [Hash<Symbol, Symbol>] the current online status (`:online`, `:idle` or `:dnd`) of the user
17
+ # on various device types (`:desktop`, `:mobile`, or `:web`). The value will be `nil` if the user is offline or invisible.
18
+ attr_reader :client_status
19
+
20
+ def initialize(data, bot)
21
+ @bot = bot
22
+
23
+ @user = bot.user(data["user"]["id"].to_i)
24
+ @status = data["status"].to_sym
25
+ @client_status = user.client_status
26
+ @server = bot.server(data["guild_id"].to_i)
27
+ end
28
+ end
29
+
30
+ # Event handler for PresenceEvent
31
+ class PresenceEventHandler < EventHandler
32
+ def matches?(event)
33
+ # Check for the proper event type
34
+ return false unless event.is_a? PresenceEvent
35
+
36
+ [
37
+ matches_all(@attributes[:from], event.user) do |a, e|
38
+ a == case a
39
+ when String
40
+ e.name
41
+ when Integer
42
+ e.id
43
+ else
44
+ e
45
+ end
46
+ end,
47
+ matches_all(@attributes[:status], event.status) do |a, e|
48
+ a == if a.is_a? String
49
+ e.to_s
50
+ else
51
+ e
52
+ end
53
+ end
54
+ ].reduce(true, &:&)
55
+ end
56
+ end
57
+
58
+ # Event raised when a user starts or stops playing a game
59
+ class PlayingEvent < Event
60
+ # @return [Server] the server on which the presence update happened.
61
+ attr_reader :server
62
+
63
+ # @return [User] the user whose status got updated.
64
+ attr_reader :user
65
+
66
+ # @return [Rubycord::Activity] The new activity
67
+ attr_reader :activity
68
+
69
+ # @!attribute [r] url
70
+ # @return [String] the URL to the stream
71
+
72
+ # @!attribute [r] details
73
+ # @return [String] what the player is currently doing (ex. game being streamed)
74
+
75
+ # @!attribute [r] type
76
+ # @return [Integer] the type of play. See {Rubycord::Activity}
77
+ delegate :url, :details, :type, to: :activity
78
+
79
+ # @return [Hash<Symbol, Symbol>] the current online status (`:online`, `:idle` or `:dnd`) of the user
80
+ # on various device types (`:desktop`, `:mobile`, or `:web`). The value will be `nil` if the user is offline or invisible.
81
+ attr_reader :client_status
82
+
83
+ def initialize(data, activity, bot)
84
+ @bot = bot
85
+ @activity = activity
86
+
87
+ @server = bot.server(data["guild_id"].to_i)
88
+ @user = bot.user(data["user"]["id"].to_i)
89
+ @client_status = @user.client_status
90
+ end
91
+
92
+ # @return [String] the name of the new game the user is playing.
93
+ def game
94
+ @activity.name
95
+ end
96
+ end
97
+
98
+ # Event handler for PlayingEvent
99
+ class PlayingEventHandler < EventHandler
100
+ def matches?(event)
101
+ # Check for the proper event type
102
+ return false unless event.is_a? PlayingEvent
103
+
104
+ [
105
+ matches_all(@attributes[:from], event.user) do |a, e|
106
+ a == case a
107
+ when String
108
+ e.name
109
+ when Integer
110
+ e.id
111
+ else
112
+ e
113
+ end
114
+ end,
115
+ matches_all(@attributes[:game], event.game) do |a, e|
116
+ a == e
117
+ end,
118
+ matches_all(@attributes[:type], event.type) do |a, e|
119
+ a == e
120
+ end,
121
+ matches_all(@attributes[:client_status], event.client_status) do |a, e|
122
+ e.slice(a.keys) == a
123
+ end
124
+ ].reduce(true, &:&)
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,45 @@
1
+ require "rubycord/events/generic"
2
+
3
+ # Event classes and handlers
4
+ module Rubycord::Events
5
+ # Event raised when any dispatch is received
6
+ class RawEvent < Event
7
+ # @return [Symbol] the type of this dispatch.
8
+ attr_reader :type
9
+ alias_method :t, :type
10
+
11
+ # @return [Hash] the data of this dispatch.
12
+ attr_reader :data
13
+ alias_method :d, :data
14
+
15
+ def initialize(type, data, bot)
16
+ @type = type
17
+ @data = data
18
+ @bot = bot
19
+ end
20
+ end
21
+
22
+ # Event handler for {RawEvent}
23
+ class RawEventHandler < EventHandler
24
+ def matches?(event)
25
+ # Check for the proper event type
26
+ return false unless event.is_a? RawEvent
27
+
28
+ [
29
+ matches_all(@attributes[:type] || @attributes[:t], event.type) do |a, e|
30
+ if a.is_a? Regexp
31
+ a.match?(e)
32
+ else
33
+ e.to_s.casecmp(a.to_s).zero?
34
+ end
35
+ end
36
+ ].reduce(true, &:&)
37
+ end
38
+ end
39
+
40
+ # Event raised when an unknown dispatch is received
41
+ class UnknownEvent < RawEvent; end
42
+
43
+ # Event handler for {UnknownEvent}
44
+ class UnknownEventHandler < RawEventHandler; end
45
+ end