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,412 @@
1
+ module Rubycord
2
+ # A message on Discord that was sent to a text channel
3
+ class Message
4
+ include IDObject
5
+
6
+ # @return [String] the content of this message.
7
+ attr_reader :content
8
+ alias_method :text, :content
9
+ alias_method :to_s, :content
10
+
11
+ # @return [Member, User] the user that sent this message. (Will be a {Member} most of the time, it should only be a
12
+ # {User} for old messages when the author has left the server since then)
13
+ attr_reader :author
14
+ alias_method :user, :author
15
+ alias_method :writer, :author
16
+
17
+ # @return [Channel] the channel in which this message was sent.
18
+ attr_reader :channel
19
+
20
+ # @return [Time] the timestamp at which this message was sent.
21
+ attr_reader :timestamp
22
+
23
+ # @return [Time] the timestamp at which this message was edited. `nil` if the message was never edited.
24
+ attr_reader :edited_timestamp
25
+ alias_method :edit_timestamp, :edited_timestamp
26
+
27
+ # @return [Array<User>] the users that were mentioned in this message.
28
+ attr_reader :mentions
29
+
30
+ # @return [Array<Role>] the roles that were mentioned in this message.
31
+ attr_reader :role_mentions
32
+
33
+ # @return [Array<Attachment>] the files attached to this message.
34
+ attr_reader :attachments
35
+
36
+ # @return [Array<Embed>] the embed objects contained in this message.
37
+ attr_reader :embeds
38
+
39
+ # @return [Array<Reaction>] the reaction objects contained in this message.
40
+ attr_reader :reactions
41
+
42
+ # @return [true, false] whether the message used Text-To-Speech (TTS) or not.
43
+ attr_reader :tts
44
+ alias_method :tts?, :tts
45
+
46
+ # @return [String] used for validating a message was sent.
47
+ attr_reader :nonce
48
+
49
+ # @return [true, false] whether the message was edited or not.
50
+ attr_reader :edited
51
+ alias_method :edited?, :edited
52
+
53
+ # @return [true, false] whether the message mentioned everyone or not.
54
+ attr_reader :mention_everyone
55
+ alias_method :mention_everyone?, :mention_everyone
56
+ alias_method :mentions_everyone?, :mention_everyone
57
+
58
+ # @return [true, false] whether the message is pinned or not.
59
+ attr_reader :pinned
60
+ alias_method :pinned?, :pinned
61
+
62
+ # @return [Integer] what the type of the message is
63
+ attr_reader :type
64
+
65
+ # @return [Server, nil] the server in which this message was sent.
66
+ attr_reader :server
67
+
68
+ # @return [Integer, nil] the webhook ID that sent this message, or `nil` if it wasn't sent through a webhook.
69
+ attr_reader :webhook_id
70
+
71
+ # @return [Array<Component>]
72
+ attr_reader :components
73
+
74
+ # @!visibility private
75
+ def initialize(data, bot)
76
+ @bot = bot
77
+ @content = data["content"]
78
+ @channel = bot.channel(data["channel_id"].to_i)
79
+ @pinned = data["pinned"]
80
+ @type = data["type"]
81
+ @tts = data["tts"]
82
+ @nonce = data["nonce"]
83
+ @mention_everyone = data["mention_everyone"]
84
+
85
+ @referenced_message = Message.new(data["referenced_message"], bot) if data["referenced_message"]
86
+ @message_reference = data["message_reference"]
87
+
88
+ @server = @channel.server
89
+
90
+ @webhook_id = data["webhook_id"]&.to_i
91
+
92
+ @author = if data["author"]
93
+ if @webhook_id
94
+ # This is a webhook user! It would be pointless to try to resolve a member here, so we just create
95
+ # a User and return that instead.
96
+ Rubycord::LOGGER.debug("Webhook user: #{data["author"]["id"]}")
97
+ User.new(data["author"].merge({"_webhook" => true}), @bot)
98
+ elsif @channel.private?
99
+ # Turn the message user into a recipient - we can't use the channel recipient
100
+ # directly because the bot may also send messages to the channel
101
+ Recipient.new(bot.user(data["author"]["id"].to_i), @channel, bot)
102
+ else
103
+ member = @channel.server.member(data["author"]["id"].to_i)
104
+
105
+ if member
106
+ member.update_data(data["member"]) if data["member"]
107
+ member.update_global_name(data["author"]["global_name"]) if data["author"]["global_name"]
108
+ else
109
+ Rubycord::LOGGER.debug("Member with ID #{data["author"]["id"]} not cached (possibly left the server).")
110
+ member = if data["member"]
111
+ member_data = data["author"].merge(data["member"])
112
+ Member.new(member_data, @server, bot)
113
+ else
114
+ @bot.ensure_user(data["author"])
115
+ end
116
+ end
117
+
118
+ member
119
+ end
120
+ end
121
+
122
+ @timestamp = Time.parse(data["timestamp"]) if data["timestamp"]
123
+ @edited_timestamp = data["edited_timestamp"].nil? ? nil : Time.parse(data["edited_timestamp"])
124
+ @edited = !@edited_timestamp.nil?
125
+ @id = data["id"].to_i
126
+
127
+ @emoji = []
128
+
129
+ @reactions = []
130
+
131
+ data["reactions"]&.each do |element|
132
+ @reactions << Reaction.new(element)
133
+ end
134
+
135
+ @mentions = []
136
+
137
+ data["mentions"]&.each do |element|
138
+ @mentions << bot.ensure_user(element)
139
+ end
140
+
141
+ @role_mentions = []
142
+
143
+ # Role mentions can only happen on public servers so make sure we only parse them there
144
+ if @channel.text?
145
+ data["mention_roles"]&.each do |element|
146
+ @role_mentions << @channel.server.role(element.to_i)
147
+ end
148
+ end
149
+
150
+ @attachments = []
151
+ @attachments = data["attachments"].map { |e| Attachment.new(e, self, @bot) } if data["attachments"]
152
+
153
+ @embeds = []
154
+ @embeds = data["embeds"].map { |e| Embed.new(e, self) } if data["embeds"]
155
+
156
+ @components = []
157
+ @components = data["components"].map { |component_data| Components.from_data(component_data, @bot) } if data["components"]
158
+ end
159
+
160
+ # Replies to this message with the specified content.
161
+ # @deprecated Please use {#respond}.
162
+ # @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
163
+ # @return (see #respond)
164
+ # @see Channel#send_message
165
+ def reply(content)
166
+ @channel.send_message(content)
167
+ end
168
+
169
+ # Responds to this message as an inline reply.
170
+ # @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
171
+ # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
172
+ # @param embed [Hash, Rubycord::Webhooks::Embed, nil] The rich embed to append to this message.
173
+ # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
174
+ # @param allowed_mentions [Hash, Rubycord::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
175
+ # @param mention_user [true, false] Whether the user that is being replied to should be pinged by the reply.
176
+ # @param components [View, Array<Hash>] Interaction components to associate with this message.
177
+ # @return (see #respond)
178
+ def reply!(content, tts: false, embed: nil, attachments: nil, allowed_mentions: {}, mention_user: false, components: nil)
179
+ allowed_mentions = {parse: []} if allowed_mentions == false
180
+ allowed_mentions = allowed_mentions.to_hash.transform_keys(&:to_sym)
181
+ allowed_mentions[:replied_user] = mention_user
182
+
183
+ respond(content, tts, embed, attachments, allowed_mentions, self, components)
184
+ end
185
+
186
+ # (see Channel#send_message)
187
+ def respond(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
188
+ @channel.send_message(content, tts, embed, attachments, allowed_mentions, message_reference, components)
189
+ end
190
+
191
+ # Edits this message to have the specified content instead.
192
+ # You can only edit your own messages.
193
+ # @param new_content [String] the new content the message should have.
194
+ # @param new_embeds [Hash, Rubycord::Webhooks::Embed, Array<Hash>, Array<Rubycord::Webhooks::Embed>, nil] The new embeds the message should have. If `nil` the message will be changed to have no embeds.
195
+ # @param new_components [View, Array<Hash>] The new components the message should have. If `nil` the message will be changed to have no components.
196
+ # @return [Message] the resulting message.
197
+ def edit(new_content, new_embeds = nil, new_components = nil)
198
+ new_embeds = (new_embeds.instance_of?(Array) ? new_embeds.map(&:to_hash) : [new_embeds&.to_hash]).compact
199
+ new_components = new_components.to_a
200
+
201
+ response = API::Channel.edit_message(@bot.token, @channel.id, @id, new_content, [], new_embeds, new_components)
202
+ Message.new(JSON.parse(response), @bot)
203
+ end
204
+
205
+ # Deletes this message.
206
+ def delete(reason = nil)
207
+ API::Channel.delete_message(@bot.token, @channel.id, @id, reason)
208
+ nil
209
+ end
210
+
211
+ # Pins this message
212
+ def pin(reason = nil)
213
+ API::Channel.pin_message(@bot.token, @channel.id, @id, reason)
214
+ @pinned = true
215
+ nil
216
+ end
217
+
218
+ # Unpins this message
219
+ def unpin(reason = nil)
220
+ API::Channel.unpin_message(@bot.token, @channel.id, @id, reason)
221
+ @pinned = false
222
+ nil
223
+ end
224
+
225
+ # Add an {Await} for a message with the same user and channel.
226
+ # @see Bot#add_await
227
+ # @deprecated Will be changed to blocking behavior in v4.0. Use {#await!} instead.
228
+ def await(key, attributes = {}, &)
229
+ @bot.add_await(key, Rubycord::Events::MessageEvent, {from: @author.id, in: @channel.id}.merge(attributes), &)
230
+ end
231
+
232
+ # Add a blocking {Await} for a message with the same user and channel.
233
+ # @see Bot#add_await!
234
+ def await!(attributes = {}, &)
235
+ @bot.add_await!(Rubycord::Events::MessageEvent, {from: @author.id, in: @channel.id}.merge(attributes), &)
236
+ end
237
+
238
+ # Add an {Await} for a reaction to be added on this message.
239
+ # @see Bot#add_await
240
+ # @deprecated Will be changed to blocking behavior in v4.0. Use {#await_reaction!} instead.
241
+ def await_reaction(key, attributes = {}, &)
242
+ @bot.add_await(key, Rubycord::Events::ReactionAddEvent, {message: @id}.merge(attributes), &)
243
+ end
244
+
245
+ # Add a blocking {Await} for a reaction to be added on this message.
246
+ # @see Bot#add_await!
247
+ def await_reaction!(attributes = {}, &)
248
+ @bot.add_await!(Rubycord::Events::ReactionAddEvent, {message: @id}.merge(attributes), &)
249
+ end
250
+
251
+ # @return [true, false] whether this message was sent by the current {Bot}.
252
+ def from_bot?
253
+ @author&.current_bot?
254
+ end
255
+
256
+ # @return [true, false] whether this message has been sent over a webhook.
257
+ def webhook?
258
+ !@webhook_id.nil?
259
+ end
260
+
261
+ # @return [Array<Emoji>] the emotes that were used/mentioned in this message.
262
+ def emoji
263
+ return if @content.nil?
264
+ return @emoji unless @emoji.empty?
265
+
266
+ @emoji = @bot.parse_mentions(@content).select { |el| el.is_a? Rubycord::Emoji }
267
+ end
268
+
269
+ # Check if any emoji were used in this message.
270
+ # @return [true, false] whether or not any emoji were used
271
+ def emoji?
272
+ emoji&.empty?
273
+ end
274
+
275
+ # Check if any reactions were used in this message.
276
+ # @return [true, false] whether or not this message has reactions
277
+ def reactions?
278
+ !@reactions.empty?
279
+ end
280
+
281
+ # Returns the reactions made by the current bot or user.
282
+ # @return [Array<Reaction>] the reactions
283
+ def my_reactions
284
+ @reactions.select(&:me)
285
+ end
286
+
287
+ # Reacts to a message.
288
+ # @param reaction [String, #to_reaction] the unicode emoji or {Emoji}
289
+ def create_reaction(reaction)
290
+ reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
291
+ API::Channel.create_reaction(@bot.token, @channel.id, @id, reaction)
292
+ nil
293
+ end
294
+
295
+ alias_method :react, :create_reaction
296
+
297
+ # Returns the list of users who reacted with a certain reaction.
298
+ # @param reaction [String, #to_reaction] the unicode emoji or {Emoji}
299
+ # @param limit [Integer] the limit of how many users to retrieve. `nil` will return all users
300
+ # @example Get all the users that reacted with a thumbs up.
301
+ # thumbs_up_reactions = message.reacted_with("\u{1F44D}")
302
+ # @return [Array<User>] the users who used this reaction
303
+ def reacted_with(reaction, limit: 100)
304
+ reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
305
+ reaction = reaction.to_s if reaction.respond_to?(:to_s)
306
+
307
+ get_reactions = proc do |fetch_limit, after_id = nil|
308
+ resp = API::Channel.get_reactions(@bot.token, @channel.id, @id, reaction, nil, after_id, fetch_limit)
309
+ return JSON.parse(resp).map { |d| User.new(d, @bot) }
310
+ end
311
+
312
+ # Can be done without pagination
313
+ return get_reactions.call(limit) if limit && limit <= 100
314
+
315
+ paginator = Paginator.new(limit, :down) do |last_page|
316
+ if last_page && last_page.count < 100
317
+ []
318
+ else
319
+ get_reactions.call(100, last_page&.last&.id)
320
+ end
321
+ end
322
+
323
+ paginator.to_a
324
+ end
325
+
326
+ # Returns a hash of all reactions to a message as keys and the users that reacted to it as values.
327
+ # @param limit [Integer] the limit of how many users to retrieve per distinct reaction emoji. `nil` will return all users
328
+ # @example Get all the users that reacted to a message for a giveaway.
329
+ # giveaway_participants = message.all_reaction_users
330
+ # @return [Hash<String => Array<User>>] A hash mapping the string representation of a
331
+ # reaction to an array of users.
332
+ def all_reaction_users(limit: 100)
333
+ all_reactions = @reactions.map { |r| {r.to_s => reacted_with(r, limit: limit)} }
334
+ all_reactions.reduce({}, :merge)
335
+ end
336
+
337
+ # Deletes a reaction made by a user on this message.
338
+ # @param user [User, String, Integer] the user or user ID who used this reaction
339
+ # @param reaction [String, #to_reaction] the reaction to remove
340
+ def delete_reaction(user, reaction)
341
+ reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
342
+ API::Channel.delete_user_reaction(@bot.token, @channel.id, @id, reaction, user.resolve_id)
343
+ end
344
+
345
+ # Deletes this client's reaction on this message.
346
+ # @param reaction [String, #to_reaction] the reaction to remove
347
+ def delete_own_reaction(reaction)
348
+ reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
349
+ API::Channel.delete_own_reaction(@bot.token, @channel.id, @id, reaction)
350
+ end
351
+
352
+ # Removes all reactions from this message.
353
+ def delete_all_reactions
354
+ API::Channel.delete_all_reactions(@bot.token, @channel.id, @id)
355
+ end
356
+
357
+ # The inspect method is overwritten to give more useful output
358
+ def inspect
359
+ "<Message content=\"#{@content}\" id=#{@id} timestamp=#{@timestamp} author=#{@author} channel=#{@channel}>"
360
+ end
361
+
362
+ # @return [String] a URL that a user can use to navigate to this message in the client
363
+ def link
364
+ "https://discord.com/channels/#{@server&.id || "@me"}/#{@channel.id}/#{@id}"
365
+ end
366
+
367
+ alias_method :jump_link, :link
368
+
369
+ # Whether or not this message was sent in reply to another message
370
+ # @return [true, false]
371
+ def reply?
372
+ !@referenced_message.nil?
373
+ end
374
+
375
+ # Whether or not this message was of type "CHAT_INPUT_COMMAND"
376
+ # @return [true, false]
377
+ def chat_input_command?
378
+ @type == 20
379
+ end
380
+
381
+ # @return [Message, nil] the Message this Message was sent in reply to.
382
+ def referenced_message
383
+ return @referenced_message if @referenced_message
384
+ return nil unless @message_reference
385
+
386
+ referenced_channel = @bot.channel(@message_reference["channel_id"])
387
+ @referenced_message = referenced_channel.message(@message_reference["message_id"])
388
+ end
389
+
390
+ # @return [Array<Components::Button>]
391
+ def buttons
392
+ results = @components.collect do |component|
393
+ case component
394
+ when Components::Button
395
+ component
396
+ when Components::ActionRow
397
+ component.buttons
398
+ end
399
+ end
400
+
401
+ results.flatten.compact
402
+ end
403
+
404
+ # to_message -> self or message
405
+ # @return [Rubycord::Message]
406
+ def to_message
407
+ self
408
+ end
409
+
410
+ alias_method :message, :to_message
411
+ end
412
+ end
@@ -0,0 +1,106 @@
1
+ module Rubycord
2
+ # A permissions overwrite, when applied to channels describes additional
3
+ # permissions a member needs to perform certain actions in context.
4
+ class Overwrite
5
+ # Types of overwrites mapped to their API value.
6
+ TYPES = {
7
+ role: 0,
8
+ member: 1
9
+ }.freeze
10
+
11
+ # @return [Integer] ID of the thing associated with this overwrite type
12
+ attr_accessor :id
13
+
14
+ # @return [Symbol] either :role or :member
15
+ attr_accessor :type
16
+
17
+ # @return [Permissions] allowed permissions for this overwrite type
18
+ attr_accessor :allow
19
+
20
+ # @return [Permissions] denied permissions for this overwrite type
21
+ attr_accessor :deny
22
+
23
+ # Creates a new Overwrite object
24
+ # @example Create an overwrite for a role that can mention everyone, send TTS messages, but can't create instant invites
25
+ # allow = Rubycord::Permissions.new
26
+ # allow.can_mention_everyone = true
27
+ # allow.can_send_tts_messages = true
28
+ #
29
+ # deny = Rubycord::Permissions.new
30
+ # deny.can_create_instant_invite = true
31
+ #
32
+ # # Find some role by name
33
+ # role = server.roles.find { |r| r.name == 'some role' }
34
+ #
35
+ # Overwrite.new(role, allow: allow, deny: deny)
36
+ # @example Create an overwrite by ID and permissions bits
37
+ # Overwrite.new(120571255635181568, type: 'member', allow: 1024, deny: 0)
38
+ # @param object [Integer, #id] the ID or object this overwrite is for
39
+ # @param type [String, Symbol, Integer] the type of object this overwrite is for (only required if object is an Integer)
40
+ # @param allow [String, Integer, Permissions] allowed permissions for this overwrite, by bits or a Permissions object
41
+ # @param deny [String, Integer, Permissions] denied permissions for this overwrite, by bits or a Permissions object
42
+ # @raise [ArgumentError] if type is not :member or :role
43
+ def initialize(object = nil, type: nil, allow: 0, deny: 0)
44
+ if type
45
+ type = TYPES.value?(type) ? TYPES.key(type) : type.to_sym
46
+ raise ArgumentError, "Overwrite type must be :member or :role" unless type
47
+ end
48
+
49
+ @id = object.respond_to?(:id) ? object.id : object
50
+
51
+ @type = case object
52
+ when User, Member, Recipient, Profile
53
+ :member
54
+ when Role
55
+ :role
56
+ else
57
+ type
58
+ end
59
+
60
+ @allow = allow.is_a?(Permissions) ? allow : Permissions.new(allow)
61
+ @deny = deny.is_a?(Permissions) ? deny : Permissions.new(deny)
62
+ end
63
+
64
+ # Comparison by attributes [:id, :type, :allow, :deny]
65
+ def ==(other)
66
+ false unless other.is_a? Rubycord::Overwrite
67
+ id == other.id &&
68
+ type == other.type &&
69
+ allow == other.allow &&
70
+ deny == other.deny
71
+ end
72
+
73
+ # @return [Overwrite] create an overwrite from a hash payload
74
+ # @!visibility private
75
+ def self.from_hash(data)
76
+ new(
77
+ data["id"].to_i,
78
+ type: TYPES.key(data["type"]),
79
+ allow: Permissions.new(data["allow"]),
80
+ deny: Permissions.new(data["deny"])
81
+ )
82
+ end
83
+
84
+ # @return [Overwrite] copies an overwrite from another Overwrite
85
+ # @!visibility private
86
+ def self.from_other(other)
87
+ new(
88
+ other.id,
89
+ type: other.type,
90
+ allow: Permissions.new(other.allow.bits),
91
+ deny: Permissions.new(other.deny.bits)
92
+ )
93
+ end
94
+
95
+ # @return [Hash] hash representation of an overwrite
96
+ # @!visibility private
97
+ def to_hash
98
+ {
99
+ id: id,
100
+ type: TYPES[type],
101
+ allow: allow.bits,
102
+ deny: deny.bits
103
+ }
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,89 @@
1
+ module Rubycord
2
+ # This class is a special variant of User that represents the bot's user profile (things like own username and the avatar).
3
+ # It can be accessed using {Bot#profile}.
4
+ class Profile < User
5
+ # Whether or not the user is the bot. The Profile can only ever be the bot user, so this always returns true.
6
+ # @return [true]
7
+ def current_bot?
8
+ true
9
+ end
10
+
11
+ # Sets the bot's username.
12
+ # @param username [String] The new username.
13
+ def username=(username)
14
+ update_profile_data(username: username)
15
+ end
16
+
17
+ alias_method :name=, :username=
18
+
19
+ # Changes the bot's avatar.
20
+ # @param avatar [String, #read] A JPG file to be used as the avatar, either
21
+ # something readable (e.g. File Object) or as a data URL.
22
+ def avatar=(avatar)
23
+ if avatar.respond_to? :read
24
+ # Set the file to binary mode if supported, so we don't get problems with Windows
25
+ avatar.binmode if avatar.respond_to?(:binmode)
26
+
27
+ avatar_string = "data:image/jpg;base64,"
28
+ avatar_string += Base64.strict_encode64(avatar.read)
29
+ update_profile_data(avatar: avatar_string)
30
+ else
31
+ update_profile_data(avatar: avatar)
32
+ end
33
+ end
34
+
35
+ # Updates the cached profile data with the new one.
36
+ # @note For internal use only.
37
+ # @!visibility private
38
+ def update_data(new_data)
39
+ @username = new_data[:username] || @username
40
+ @avatar_id = new_data[:avatar_id] || @avatar_id
41
+ end
42
+
43
+ # Sets the user status setting to Online.
44
+ # @note Only usable on User accounts.
45
+ def online
46
+ update_profile_status_setting("online")
47
+ end
48
+
49
+ # Sets the user status setting to Idle.
50
+ # @note Only usable on User accounts.
51
+ def idle
52
+ update_profile_status_setting("idle")
53
+ end
54
+
55
+ # Sets the user status setting to Do Not Disturb.
56
+ # @note Only usable on User accounts.
57
+ def dnd
58
+ update_profile_status_setting("dnd")
59
+ end
60
+
61
+ alias_method(:busy, :dnd)
62
+
63
+ # Sets the user status setting to Invisible.
64
+ # @note Only usable on User accounts.
65
+ def invisible
66
+ update_profile_status_setting("invisible")
67
+ end
68
+
69
+ # The inspect method is overwritten to give more useful output
70
+ def inspect
71
+ "<Profile user=#{super}>"
72
+ end
73
+
74
+ private
75
+
76
+ # Internal handler for updating the user's status setting
77
+ def update_profile_status_setting(status)
78
+ API::User.change_status_setting(@bot.token, status)
79
+ end
80
+
81
+ def update_profile_data(new_data)
82
+ API::User.update_profile(@bot.token,
83
+ nil, nil,
84
+ new_data[:username] || @username,
85
+ new_data.key?(:avatar) ? new_data[:avatar] : @avatar_id)
86
+ update_data(new_data)
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,31 @@
1
+ module Rubycord
2
+ # A reaction to a message.
3
+ class Reaction
4
+ # @return [Integer] the amount of users who have reacted with this reaction
5
+ attr_reader :count
6
+
7
+ # @return [true, false] whether the current bot or user used this reaction
8
+ attr_reader :me
9
+ alias_method :me?, :me
10
+
11
+ # @return [Integer] the ID of the emoji, if it was custom
12
+ attr_reader :id
13
+
14
+ # @return [String] the name or unicode representation of the emoji
15
+ attr_reader :name
16
+
17
+ def initialize(data)
18
+ @count = data["count"]
19
+ @me = data["me"]
20
+ @id = data["emoji"]["id"]&.to_i
21
+ @name = data["emoji"]["name"]
22
+ end
23
+
24
+ # Converts this Reaction into a string that can be sent back to Discord in other reaction endpoints.
25
+ # If ID is present, it will be rendered into the form of `name:id`.
26
+ # @return [String] the name of this reaction, including the ID if it is a custom emoji
27
+ def to_s
28
+ id.nil? ? name : "#{name}:#{id}"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ module Rubycord
2
+ # Recipients are members on private channels - they exist for completeness purposes, but all
3
+ # the attributes will be empty.
4
+ class Recipient < DelegateClass(User)
5
+ include MemberAttributes
6
+
7
+ # @return [Channel] the private channel this recipient is the recipient of.
8
+ attr_reader :channel
9
+
10
+ # @!visibility private
11
+ def initialize(user, channel, bot)
12
+ @bot = bot
13
+ @channel = channel
14
+ raise ArgumentError, "Tried to create a recipient for a public channel!" unless @channel.private?
15
+
16
+ @user = user
17
+ super(@user)
18
+
19
+ # Member attributes
20
+ @mute = @deaf = @self_mute = @self_deaf = false
21
+ @voice_channel = nil
22
+ @server = nil
23
+ @roles = []
24
+ @joined_at = @channel.creation_time
25
+ end
26
+
27
+ # Overwriting inspect for debug purposes
28
+ def inspect
29
+ "<Recipient user=#{@user.inspect} channel=#{@channel.inspect}>"
30
+ end
31
+ end
32
+ end