rubycord 1.0.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 (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