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.
- checksums.yaml +7 -0
- data/lib/rubycord/allowed_mentions.rb +34 -0
- data/lib/rubycord/api/application.rb +200 -0
- data/lib/rubycord/api/channel.rb +597 -0
- data/lib/rubycord/api/interaction.rb +52 -0
- data/lib/rubycord/api/invite.rb +42 -0
- data/lib/rubycord/api/server.rb +557 -0
- data/lib/rubycord/api/user.rb +153 -0
- data/lib/rubycord/api/webhook.rb +138 -0
- data/lib/rubycord/api.rb +356 -0
- data/lib/rubycord/await.rb +49 -0
- data/lib/rubycord/bot.rb +1757 -0
- data/lib/rubycord/cache.rb +259 -0
- data/lib/rubycord/colour_rgb.rb +41 -0
- data/lib/rubycord/commands/command_bot.rb +519 -0
- data/lib/rubycord/commands/container.rb +110 -0
- data/lib/rubycord/commands/events.rb +9 -0
- data/lib/rubycord/commands/parser.rb +325 -0
- data/lib/rubycord/commands/rate_limiter.rb +142 -0
- data/lib/rubycord/container.rb +753 -0
- data/lib/rubycord/data/activity.rb +269 -0
- data/lib/rubycord/data/application.rb +48 -0
- data/lib/rubycord/data/attachment.rb +109 -0
- data/lib/rubycord/data/audit_logs.rb +343 -0
- data/lib/rubycord/data/channel.rb +996 -0
- data/lib/rubycord/data/component.rb +227 -0
- data/lib/rubycord/data/embed.rb +249 -0
- data/lib/rubycord/data/emoji.rb +80 -0
- data/lib/rubycord/data/integration.rb +120 -0
- data/lib/rubycord/data/interaction.rb +798 -0
- data/lib/rubycord/data/invite.rb +135 -0
- data/lib/rubycord/data/member.rb +370 -0
- data/lib/rubycord/data/message.rb +412 -0
- data/lib/rubycord/data/overwrite.rb +106 -0
- data/lib/rubycord/data/profile.rb +89 -0
- data/lib/rubycord/data/reaction.rb +31 -0
- data/lib/rubycord/data/recipient.rb +32 -0
- data/lib/rubycord/data/role.rb +246 -0
- data/lib/rubycord/data/server.rb +1002 -0
- data/lib/rubycord/data/user.rb +261 -0
- data/lib/rubycord/data/voice_region.rb +43 -0
- data/lib/rubycord/data/voice_state.rb +39 -0
- data/lib/rubycord/data/webhook.rb +232 -0
- data/lib/rubycord/data.rb +40 -0
- data/lib/rubycord/errors.rb +737 -0
- data/lib/rubycord/events/await.rb +46 -0
- data/lib/rubycord/events/bans.rb +58 -0
- data/lib/rubycord/events/channels.rb +186 -0
- data/lib/rubycord/events/generic.rb +126 -0
- data/lib/rubycord/events/guilds.rb +191 -0
- data/lib/rubycord/events/interactions.rb +480 -0
- data/lib/rubycord/events/invites.rb +123 -0
- data/lib/rubycord/events/lifetime.rb +29 -0
- data/lib/rubycord/events/members.rb +91 -0
- data/lib/rubycord/events/message.rb +337 -0
- data/lib/rubycord/events/presence.rb +127 -0
- data/lib/rubycord/events/raw.rb +45 -0
- data/lib/rubycord/events/reactions.rb +156 -0
- data/lib/rubycord/events/roles.rb +86 -0
- data/lib/rubycord/events/threads.rb +94 -0
- data/lib/rubycord/events/typing.rb +70 -0
- data/lib/rubycord/events/voice_server_update.rb +45 -0
- data/lib/rubycord/events/voice_state_update.rb +103 -0
- data/lib/rubycord/events/webhooks.rb +62 -0
- data/lib/rubycord/gateway.rb +867 -0
- data/lib/rubycord/id_object.rb +37 -0
- data/lib/rubycord/light/data.rb +60 -0
- data/lib/rubycord/light/integrations.rb +71 -0
- data/lib/rubycord/light/light_bot.rb +56 -0
- data/lib/rubycord/light.rb +6 -0
- data/lib/rubycord/logger.rb +118 -0
- data/lib/rubycord/paginator.rb +55 -0
- data/lib/rubycord/permissions.rb +251 -0
- data/lib/rubycord/version.rb +5 -0
- data/lib/rubycord/voice/encoder.rb +113 -0
- data/lib/rubycord/voice/network.rb +366 -0
- data/lib/rubycord/voice/sodium.rb +96 -0
- data/lib/rubycord/voice/voice_bot.rb +408 -0
- data/lib/rubycord/webhooks/builder.rb +100 -0
- data/lib/rubycord/webhooks/client.rb +132 -0
- data/lib/rubycord/webhooks/embeds.rb +248 -0
- data/lib/rubycord/webhooks/modal.rb +78 -0
- data/lib/rubycord/webhooks/version.rb +7 -0
- data/lib/rubycord/webhooks/view.rb +192 -0
- data/lib/rubycord/webhooks.rb +12 -0
- data/lib/rubycord/websocket.rb +70 -0
- data/lib/rubycord.rb +140 -0
- 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
|