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,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