discordrb 3.3.0 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of discordrb might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.circleci/config.yml +126 -0
- data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +0 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- data/.github/pull_request_template.md +37 -0
- data/.rubocop.yml +34 -37
- data/.travis.yml +5 -6
- data/CHANGELOG.md +472 -347
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +61 -79
- data/Rakefile +2 -0
- data/bin/console +1 -0
- data/discordrb-webhooks.gemspec +6 -6
- data/discordrb.gemspec +17 -17
- data/lib/discordrb.rb +73 -0
- data/lib/discordrb/allowed_mentions.rb +36 -0
- data/lib/discordrb/api.rb +40 -15
- data/lib/discordrb/api/channel.rb +57 -39
- data/lib/discordrb/api/invite.rb +3 -3
- data/lib/discordrb/api/server.rb +55 -50
- data/lib/discordrb/api/user.rb +8 -8
- data/lib/discordrb/api/webhook.rb +6 -6
- data/lib/discordrb/await.rb +0 -1
- data/lib/discordrb/bot.rb +164 -72
- data/lib/discordrb/cache.rb +4 -2
- data/lib/discordrb/colour_rgb.rb +43 -0
- data/lib/discordrb/commands/command_bot.rb +22 -6
- data/lib/discordrb/commands/container.rb +20 -23
- data/lib/discordrb/commands/parser.rb +18 -18
- data/lib/discordrb/commands/rate_limiter.rb +3 -2
- data/lib/discordrb/container.rb +77 -17
- data/lib/discordrb/data.rb +25 -4180
- data/lib/discordrb/data/activity.rb +264 -0
- data/lib/discordrb/data/application.rb +50 -0
- data/lib/discordrb/data/attachment.rb +56 -0
- data/lib/discordrb/data/audit_logs.rb +345 -0
- data/lib/discordrb/data/channel.rb +849 -0
- data/lib/discordrb/data/embed.rb +251 -0
- data/lib/discordrb/data/emoji.rb +82 -0
- data/lib/discordrb/data/integration.rb +83 -0
- data/lib/discordrb/data/invite.rb +137 -0
- data/lib/discordrb/data/member.rb +297 -0
- data/lib/discordrb/data/message.rb +334 -0
- data/lib/discordrb/data/overwrite.rb +102 -0
- data/lib/discordrb/data/profile.rb +91 -0
- data/lib/discordrb/data/reaction.rb +33 -0
- data/lib/discordrb/data/recipient.rb +34 -0
- data/lib/discordrb/data/role.rb +191 -0
- data/lib/discordrb/data/server.rb +1002 -0
- data/lib/discordrb/data/user.rb +204 -0
- data/lib/discordrb/data/voice_region.rb +45 -0
- data/lib/discordrb/data/voice_state.rb +41 -0
- data/lib/discordrb/data/webhook.rb +145 -0
- data/lib/discordrb/errors.rb +2 -1
- data/lib/discordrb/events/bans.rb +7 -5
- data/lib/discordrb/events/channels.rb +2 -0
- data/lib/discordrb/events/guilds.rb +16 -9
- data/lib/discordrb/events/invites.rb +125 -0
- data/lib/discordrb/events/members.rb +6 -2
- data/lib/discordrb/events/message.rb +69 -27
- data/lib/discordrb/events/presence.rb +14 -4
- data/lib/discordrb/events/raw.rb +1 -3
- data/lib/discordrb/events/reactions.rb +49 -3
- data/lib/discordrb/events/typing.rb +6 -4
- data/lib/discordrb/events/voice_server_update.rb +47 -0
- data/lib/discordrb/events/voice_state_update.rb +15 -10
- data/lib/discordrb/events/webhooks.rb +9 -6
- data/lib/discordrb/gateway.rb +72 -57
- data/lib/discordrb/id_object.rb +39 -0
- data/lib/discordrb/light/integrations.rb +1 -1
- data/lib/discordrb/light/light_bot.rb +1 -1
- data/lib/discordrb/logger.rb +4 -4
- data/lib/discordrb/paginator.rb +57 -0
- data/lib/discordrb/permissions.rb +103 -8
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +3 -3
- data/lib/discordrb/voice/network.rb +84 -43
- data/lib/discordrb/voice/sodium.rb +96 -0
- data/lib/discordrb/voice/voice_bot.rb +34 -26
- metadata +93 -55
data/lib/discordrb/data.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require 'discordrb/allowed_mentions'
|
5
4
|
require 'discordrb/permissions'
|
5
|
+
require 'discordrb/id_object'
|
6
|
+
require 'discordrb/colour_rgb'
|
6
7
|
require 'discordrb/errors'
|
7
8
|
require 'discordrb/api'
|
8
9
|
require 'discordrb/api/channel'
|
@@ -11,4184 +12,28 @@ require 'discordrb/api/invite'
|
|
11
12
|
require 'discordrb/api/user'
|
12
13
|
require 'discordrb/api/webhook'
|
13
14
|
require 'discordrb/webhooks/embeds'
|
15
|
+
require 'discordrb/paginator'
|
14
16
|
require 'time'
|
15
17
|
require 'base64'
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
lines = msg.lines
|
39
|
-
|
40
|
-
# Turn the message into a "triangle" of consecutively longer slices, for example the array [1,2,3,4] would become
|
41
|
-
# [
|
42
|
-
# [1],
|
43
|
-
# [1, 2],
|
44
|
-
# [1, 2, 3],
|
45
|
-
# [1, 2, 3, 4]
|
46
|
-
# ]
|
47
|
-
tri = [*0..(lines.length - 1)].map { |i| lines.combination(i + 1).first }
|
48
|
-
|
49
|
-
# Join the individual elements together to get an array of strings with consecutively more lines
|
50
|
-
joined = tri.map(&:join)
|
51
|
-
|
52
|
-
# Find the largest element that is still below the character limit, or if none such element exists return the first
|
53
|
-
ideal = joined.max_by { |e| e.length > CHARACTER_LIMIT ? -1 : e.length }
|
54
|
-
|
55
|
-
# If it's still larger than the character limit (none was smaller than it) split it into slices with the length
|
56
|
-
# being the character limit, otherwise just return an array with one element
|
57
|
-
ideal_ary = ideal.length > CHARACTER_LIMIT ? ideal.chars.each_slice(CHARACTER_LIMIT).map(&:join) : [ideal]
|
58
|
-
|
59
|
-
# Slice off the ideal part and strip newlines
|
60
|
-
rest = msg[ideal.length..-1].strip
|
61
|
-
|
62
|
-
# If none remains, return an empty array -> we're done
|
63
|
-
return [] unless rest
|
64
|
-
|
65
|
-
# Otherwise, call the method recursively to split the rest of the string and add it onto the ideal array
|
66
|
-
ideal_ary + split_message(rest)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Mixin for objects that have IDs
|
70
|
-
module IDObject
|
71
|
-
# @return [Integer] the ID which uniquely identifies this object across Discord.
|
72
|
-
attr_reader :id
|
73
|
-
alias_method :resolve_id, :id
|
74
|
-
alias_method :hash, :id
|
75
|
-
|
76
|
-
# ID based comparison
|
77
|
-
def ==(other)
|
78
|
-
Discordrb.id_compare(@id, other)
|
79
|
-
end
|
80
|
-
|
81
|
-
alias_method :eql?, :==
|
82
|
-
|
83
|
-
# Estimates the time this object was generated on based on the beginning of the ID. This is fairly accurate but
|
84
|
-
# shouldn't be relied on as Discord might change its algorithm at any time
|
85
|
-
# @return [Time] when this object was created at
|
86
|
-
def creation_time
|
87
|
-
# Milliseconds
|
88
|
-
ms = (@id >> 22) + DISCORD_EPOCH
|
89
|
-
Time.at(ms / 1000.0)
|
90
|
-
end
|
91
|
-
|
92
|
-
# Creates an artificial snowflake at the given point in time. Useful for comparing against.
|
93
|
-
# @param time [Time] The time the snowflake should represent.
|
94
|
-
# @return [Integer] a snowflake with the timestamp data as the given time
|
95
|
-
def self.synthesise(time)
|
96
|
-
ms = (time.to_f * 1000).to_i
|
97
|
-
(ms - DISCORD_EPOCH) << 22
|
98
|
-
end
|
99
|
-
|
100
|
-
class << self
|
101
|
-
alias_method :synthesize, :synthesise
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
# Mixin for the attributes users should have
|
106
|
-
module UserAttributes
|
107
|
-
# @return [String] this user's username
|
108
|
-
attr_reader :username
|
109
|
-
alias_method :name, :username
|
110
|
-
|
111
|
-
# @return [String] this user's discriminator which is used internally to identify users with identical usernames.
|
112
|
-
attr_reader :discriminator
|
113
|
-
alias_method :discrim, :discriminator
|
114
|
-
alias_method :tag, :discriminator
|
115
|
-
alias_method :discord_tag, :discriminator
|
116
|
-
|
117
|
-
# @return [true, false] whether this user is a Discord bot account
|
118
|
-
attr_reader :bot_account
|
119
|
-
alias_method :bot_account?, :bot_account
|
120
|
-
|
121
|
-
# @return [String] the ID of this user's current avatar, can be used to generate an avatar URL.
|
122
|
-
# @see #avatar_url
|
123
|
-
attr_accessor :avatar_id
|
124
|
-
|
125
|
-
# Utility function to mention users in messages
|
126
|
-
# @return [String] the mention code in the form of <@id>
|
127
|
-
def mention
|
128
|
-
"<@#{@id}>"
|
129
|
-
end
|
130
|
-
|
131
|
-
# Utility function to get Discord's distinct representation of a user, i.e. username + discriminator
|
132
|
-
# @return [String] distinct representation of user
|
133
|
-
def distinct
|
134
|
-
"#{@username}##{@discriminator}"
|
135
|
-
end
|
136
|
-
|
137
|
-
# Utility function to get a user's avatar URL.
|
138
|
-
# @param format [String, nil] If `nil`, the URL will default to `webp` for static avatars, and will detect if the user has a `gif` avatar. You can otherwise specify one of `webp`, `jpg`, `png`, or `gif` to override this. Will always be PNG for default avatars.
|
139
|
-
# @return [String] the URL to the avatar image.
|
140
|
-
def avatar_url(format = nil)
|
141
|
-
return API::User.default_avatar(@discriminator) unless @avatar_id
|
142
|
-
API::User.avatar_url(@id, @avatar_id, format)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
# User on Discord, including internal data like discriminators
|
147
|
-
class User
|
148
|
-
include IDObject
|
149
|
-
include UserAttributes
|
150
|
-
|
151
|
-
# @return [Symbol] the current online status of the user (`:online`, `:offline` or `:idle`)
|
152
|
-
attr_reader :status
|
153
|
-
|
154
|
-
# @return [String, nil] the game the user is currently playing, or `nil` if none is being played.
|
155
|
-
attr_reader :game
|
156
|
-
|
157
|
-
# @return [String, nil] the URL to the stream, if the user is currently streaming something.
|
158
|
-
attr_reader :stream_url
|
159
|
-
|
160
|
-
# @return [String, Integer, nil] the type of the stream. Can technically be set to anything, most of the time it
|
161
|
-
# will be 0 for no stream or 1 for Twitch streams.
|
162
|
-
attr_reader :stream_type
|
163
|
-
|
164
|
-
def initialize(data, bot)
|
165
|
-
@bot = bot
|
166
|
-
|
167
|
-
@username = data['username']
|
168
|
-
@id = data['id'].to_i
|
169
|
-
@discriminator = data['discriminator']
|
170
|
-
@avatar_id = data['avatar']
|
171
|
-
@roles = {}
|
172
|
-
|
173
|
-
@bot_account = false
|
174
|
-
@bot_account = true if data['bot']
|
175
|
-
|
176
|
-
@status = :offline
|
177
|
-
end
|
178
|
-
|
179
|
-
# Get a user's PM channel or send them a PM
|
180
|
-
# @overload pm
|
181
|
-
# Creates a private message channel for this user or returns an existing one if it already exists
|
182
|
-
# @return [Channel] the PM channel to this user.
|
183
|
-
# @overload pm(content)
|
184
|
-
# Sends a private to this user.
|
185
|
-
# @param content [String] The content to send.
|
186
|
-
# @return [Message] the message sent to this user.
|
187
|
-
def pm(content = nil)
|
188
|
-
if content
|
189
|
-
# Recursively call pm to get the channel, then send a message to it
|
190
|
-
channel = pm
|
191
|
-
channel.send_message(content)
|
192
|
-
else
|
193
|
-
# If no message was specified, return the PM channel
|
194
|
-
@bot.pm_channel(@id)
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
alias_method :dm, :pm
|
199
|
-
|
200
|
-
# Send the user a file.
|
201
|
-
# @param file [File] The file to send to the user
|
202
|
-
# @param caption [String] The caption of the file being sent
|
203
|
-
# @return [Message] the message sent to this user.
|
204
|
-
# @example Send a file from disk
|
205
|
-
# user.send_file(File.open('rubytaco.png', 'r'))
|
206
|
-
def send_file(file, caption = nil)
|
207
|
-
pm.send_file(file, caption: caption)
|
208
|
-
end
|
209
|
-
|
210
|
-
# Set the user's name
|
211
|
-
# @note for internal use only
|
212
|
-
# @!visibility private
|
213
|
-
def update_username(username)
|
214
|
-
@username = username
|
215
|
-
end
|
216
|
-
|
217
|
-
# Set the user's presence data
|
218
|
-
# @note for internal use only
|
219
|
-
# @!visibility private
|
220
|
-
def update_presence(data)
|
221
|
-
@status = data['status'].to_sym
|
222
|
-
|
223
|
-
if data['game']
|
224
|
-
game = data['game']
|
225
|
-
|
226
|
-
@game = game['name']
|
227
|
-
@stream_url = game['url']
|
228
|
-
@stream_type = game['type']
|
229
|
-
else
|
230
|
-
@game = @stream_url = @stream_type = nil
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
# Add an await for a message from this user. Specifically, this adds a global await for a MessageEvent with this
|
235
|
-
# user's ID as a :from attribute.
|
236
|
-
# @see Bot#add_await
|
237
|
-
def await(key, attributes = {}, &block)
|
238
|
-
@bot.add_await(key, Discordrb::Events::MessageEvent, { from: @id }.merge(attributes), &block)
|
239
|
-
end
|
240
|
-
|
241
|
-
# Add a blocking await for a message from this user. Specifically, this adds a global await for a MessageEvent with this
|
242
|
-
# user's ID as a :from attribute.
|
243
|
-
# @see Bot#add_await!
|
244
|
-
def await!(attributes = {})
|
245
|
-
@bot.add_await!(Discordrb::Events::MessageEvent, { from: @id }.merge(attributes))
|
246
|
-
end
|
247
|
-
|
248
|
-
# Gets the member this user is on a server
|
249
|
-
# @param server [Server] The server to get the member for
|
250
|
-
# @return [Member] this user as a member on a particular server
|
251
|
-
def on(server)
|
252
|
-
id = server.resolve_id
|
253
|
-
@bot.server(id).member(@id)
|
254
|
-
end
|
255
|
-
|
256
|
-
# Is the user the bot?
|
257
|
-
# @return [true, false] whether this user is the bot
|
258
|
-
def current_bot?
|
259
|
-
@bot.profile.id == @id
|
260
|
-
end
|
261
|
-
|
262
|
-
# @return [true, false] whether this user is a fake user for a webhook message
|
263
|
-
def webhook?
|
264
|
-
@discriminator == Message::ZERO_DISCRIM
|
265
|
-
end
|
266
|
-
|
267
|
-
%i[offline idle online].each do |e|
|
268
|
-
define_method(e.to_s + '?') do
|
269
|
-
@status.to_sym == e
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
# The inspect method is overwritten to give more useful output
|
274
|
-
def inspect
|
275
|
-
"<User username=#{@username} id=#{@id} discriminator=#{@discriminator}>"
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
# OAuth Application information
|
280
|
-
class Application
|
281
|
-
include IDObject
|
282
|
-
|
283
|
-
# @return [String] the application name
|
284
|
-
attr_reader :name
|
285
|
-
|
286
|
-
# @return [String] the application description
|
287
|
-
attr_reader :description
|
288
|
-
|
289
|
-
# @return [Array<String>] the application's origins permitted to use RPC
|
290
|
-
attr_reader :rpc_origins
|
291
|
-
|
292
|
-
# @return [Integer]
|
293
|
-
attr_reader :flags
|
294
|
-
|
295
|
-
# Gets the user object of the owner. May be limited to username, discriminator,
|
296
|
-
# ID, and avatar if the bot cannot reach the owner.
|
297
|
-
# @return [User] the user object of the owner
|
298
|
-
attr_reader :owner
|
299
|
-
|
300
|
-
def initialize(data, bot)
|
301
|
-
@bot = bot
|
302
|
-
|
303
|
-
@name = data['name']
|
304
|
-
@id = data['id'].to_i
|
305
|
-
@description = data['description']
|
306
|
-
@icon_id = data['icon']
|
307
|
-
@rpc_origins = data['rpc_origins']
|
308
|
-
@flags = data['flags']
|
309
|
-
@owner = @bot.ensure_user(data['owner'])
|
310
|
-
end
|
311
|
-
|
312
|
-
# Utility function to get a application's icon URL.
|
313
|
-
# @return [String, nil] the URL of the icon image (nil if no image is set).
|
314
|
-
def icon_url
|
315
|
-
return nil if @icon_id.nil?
|
316
|
-
API.app_icon_url(@id, @icon_id)
|
317
|
-
end
|
318
|
-
|
319
|
-
# The inspect method is overwritten to give more useful output
|
320
|
-
def inspect
|
321
|
-
"<Application name=#{@name} id=#{@id}>"
|
322
|
-
end
|
323
|
-
end
|
324
|
-
|
325
|
-
# Mixin for the attributes members and private members should have
|
326
|
-
module MemberAttributes
|
327
|
-
# @return [Time] when this member joined the server.
|
328
|
-
attr_reader :joined_at
|
329
|
-
|
330
|
-
# @return [String, nil] the nickname this member has, or `nil` if it has none.
|
331
|
-
attr_reader :nick
|
332
|
-
alias_method :nickname, :nick
|
333
|
-
|
334
|
-
# @return [Array<Role>] the roles this member has.
|
335
|
-
attr_reader :roles
|
336
|
-
|
337
|
-
# @return [Server] the server this member is on.
|
338
|
-
attr_reader :server
|
339
|
-
end
|
340
|
-
|
341
|
-
# Mixin to calculate resulting permissions from overrides etc.
|
342
|
-
module PermissionCalculator
|
343
|
-
# Checks whether this user can do the particular action, regardless of whether it has the permission defined,
|
344
|
-
# through for example being the server owner or having the Manage Roles permission
|
345
|
-
# @param action [Symbol] The permission that should be checked. See also {Permissions::Flags} for a list.
|
346
|
-
# @param channel [Channel, nil] If channel overrides should be checked too, this channel specifies where the overrides should be checked.
|
347
|
-
# @example Check if the bot can send messages to a specific channel in a server.
|
348
|
-
# bot_profile = bot.profile.on(event.server)
|
349
|
-
# can_send_messages = bot_profile.permission?(:send_messages, channel)
|
350
|
-
# @return [true, false] whether or not this user has the permission.
|
351
|
-
def permission?(action, channel = nil)
|
352
|
-
# If the member is the server owner, it irrevocably has all permissions.
|
353
|
-
return true if owner?
|
354
|
-
|
355
|
-
# First, check whether the user has Manage Roles defined.
|
356
|
-
# (Coincidentally, Manage Permissions is the same permission as Manage Roles, and a
|
357
|
-
# Manage Permissions deny overwrite will override Manage Roles, so we can just check for
|
358
|
-
# Manage Roles once and call it a day.)
|
359
|
-
return true if defined_permission?(:administrator, channel)
|
360
|
-
|
361
|
-
# Otherwise, defer to defined_permission
|
362
|
-
defined_permission?(action, channel)
|
363
|
-
end
|
364
|
-
|
365
|
-
# Checks whether this user has a particular permission defined (i.e. not implicit, through for example
|
366
|
-
# Manage Roles)
|
367
|
-
# @param action [Symbol] The permission that should be checked. See also {Permissions::Flags} for a list.
|
368
|
-
# @param channel [Channel, nil] If channel overrides should be checked too, this channel specifies where the overrides should be checked.
|
369
|
-
# @example Check if a member has the Manage Channels permission defined in the server.
|
370
|
-
# has_manage_channels = member.defined_permission?(:manage_channels)
|
371
|
-
# @return [true, false] whether or not this user has the permission defined.
|
372
|
-
def defined_permission?(action, channel = nil)
|
373
|
-
# Get the permission the user's roles have
|
374
|
-
role_permission = defined_role_permission?(action, channel)
|
375
|
-
|
376
|
-
# Once we have checked the role permission, we have to check the channel overrides for the
|
377
|
-
# specific user
|
378
|
-
user_specific_override = permission_overwrite(action, channel, id) # Use the ID reader as members have no ID instance variable
|
379
|
-
|
380
|
-
# Merge the two permissions - if an override is defined, it has to be allow, otherwise we only care about the role
|
381
|
-
return role_permission unless user_specific_override
|
382
|
-
user_specific_override == :allow
|
383
|
-
end
|
384
|
-
|
385
|
-
# Define methods for querying permissions
|
386
|
-
Discordrb::Permissions::Flags.each_value do |flag|
|
387
|
-
define_method "can_#{flag}?" do |channel = nil|
|
388
|
-
permission? flag, channel
|
389
|
-
end
|
390
|
-
end
|
391
|
-
|
392
|
-
alias_method :can_administrate?, :can_administrator?
|
393
|
-
|
394
|
-
private
|
395
|
-
|
396
|
-
def defined_role_permission?(action, channel)
|
397
|
-
roles_to_check = [@server.everyone_role] + @roles
|
398
|
-
|
399
|
-
# For each role, check if
|
400
|
-
# (1) the channel explicitly allows or permits an action for the role and
|
401
|
-
# (2) if the user is allowed to do the action if the channel doesn't specify
|
402
|
-
roles_to_check.reduce(false) do |can_act, role|
|
403
|
-
# Get the override defined for the role on the channel
|
404
|
-
channel_allow = permission_overwrite(action, channel, role.id)
|
405
|
-
can_act = if channel_allow
|
406
|
-
# If the channel has an override, check whether it is an allow - if yes,
|
407
|
-
# the user can act, if not, it can't
|
408
|
-
channel_allow == :allow
|
409
|
-
else
|
410
|
-
# Otherwise defer to the role
|
411
|
-
role.permissions.instance_variable_get("@#{action}") || can_act
|
412
|
-
end
|
413
|
-
can_act
|
414
|
-
end
|
415
|
-
end
|
416
|
-
|
417
|
-
def permission_overwrite(action, channel, id)
|
418
|
-
# If no overwrites are defined, or no channel is set, no overwrite will be present
|
419
|
-
return nil unless channel && channel.permission_overwrites[id]
|
420
|
-
|
421
|
-
# Otherwise, check the allow and deny objects
|
422
|
-
allow = channel.permission_overwrites[id].allow
|
423
|
-
deny = channel.permission_overwrites[id].deny
|
424
|
-
if allow.instance_variable_get("@#{action}")
|
425
|
-
:allow
|
426
|
-
elsif deny.instance_variable_get("@#{action}")
|
427
|
-
:deny
|
428
|
-
end
|
429
|
-
|
430
|
-
# If there's no variable defined, nil will implicitly be returned
|
431
|
-
end
|
432
|
-
end
|
433
|
-
|
434
|
-
# A voice state represents the state of a member's connection to a voice channel. It includes data like the voice
|
435
|
-
# channel the member is connected to and mute/deaf flags.
|
436
|
-
class VoiceState
|
437
|
-
# @return [Integer] the ID of the user whose voice state is represented by this object.
|
438
|
-
attr_reader :user_id
|
439
|
-
|
440
|
-
# @return [true, false] whether this voice state's member is muted server-wide.
|
441
|
-
attr_reader :mute
|
442
|
-
|
443
|
-
# @return [true, false] whether this voice state's member is deafened server-wide.
|
444
|
-
attr_reader :deaf
|
445
|
-
|
446
|
-
# @return [true, false] whether this voice state's member has muted themselves.
|
447
|
-
attr_reader :self_mute
|
448
|
-
|
449
|
-
# @return [true, false] whether this voice state's member has deafened themselves.
|
450
|
-
attr_reader :self_deaf
|
451
|
-
|
452
|
-
# @return [Channel] the voice channel this voice state's member is in.
|
453
|
-
attr_reader :voice_channel
|
454
|
-
|
455
|
-
# @!visibility private
|
456
|
-
def initialize(user_id)
|
457
|
-
@user_id = user_id
|
458
|
-
end
|
459
|
-
|
460
|
-
# Update this voice state with new data from Discord
|
461
|
-
# @note For internal use only.
|
462
|
-
# @!visibility private
|
463
|
-
def update(channel, mute, deaf, self_mute, self_deaf)
|
464
|
-
@voice_channel = channel
|
465
|
-
@mute = mute
|
466
|
-
@deaf = deaf
|
467
|
-
@self_mute = self_mute
|
468
|
-
@self_deaf = self_deaf
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
|
-
# Voice regions are the locations of servers that handle voice communication in Discord
|
473
|
-
class VoiceRegion
|
474
|
-
# @return [String] unique ID for the region
|
475
|
-
attr_reader :id
|
476
|
-
alias_method :to_s, :id
|
477
|
-
|
478
|
-
# @return [String] name of the region
|
479
|
-
attr_reader :name
|
480
|
-
|
481
|
-
# @return [String] an example hostname for the region
|
482
|
-
attr_reader :sample_hostname
|
483
|
-
|
484
|
-
# @return [Integer] an example port for the region
|
485
|
-
attr_reader :sample_port
|
486
|
-
|
487
|
-
# @return [true, false] if this is a VIP-only server
|
488
|
-
attr_reader :vip
|
489
|
-
|
490
|
-
# @return [true, false] if this voice server is the closest to the client
|
491
|
-
attr_reader :optimal
|
492
|
-
|
493
|
-
# @return [true, false] whether this is a deprecated voice region (avoid switching to these)
|
494
|
-
attr_reader :deprecated
|
495
|
-
|
496
|
-
# @return [true, false] whether this is a custom voice region (used for events/etc)
|
497
|
-
attr_reader :custom
|
498
|
-
|
499
|
-
def initialize(data)
|
500
|
-
@id = data['id']
|
501
|
-
|
502
|
-
@name = data['name']
|
503
|
-
|
504
|
-
@sample_hostname = data['sample_hostname']
|
505
|
-
@sample_port = data['sample_port']
|
506
|
-
|
507
|
-
@vip = data['vip']
|
508
|
-
@optimal = data['optimal']
|
509
|
-
@deprecated = data['deprecated']
|
510
|
-
@custom = data['custom']
|
511
|
-
end
|
512
|
-
end
|
513
|
-
|
514
|
-
# A member is a user on a server. It differs from regular users in that it has roles, voice statuses and things like
|
515
|
-
# that.
|
516
|
-
class Member < DelegateClass(User)
|
517
|
-
# @return [true, false] whether this member is muted server-wide.
|
518
|
-
def mute
|
519
|
-
voice_state_attribute(:mute)
|
520
|
-
end
|
521
|
-
|
522
|
-
# @return [true, false] whether this member is deafened server-wide.
|
523
|
-
def deaf
|
524
|
-
voice_state_attribute(:deaf)
|
525
|
-
end
|
526
|
-
|
527
|
-
# @return [true, false] whether this member has muted themselves.
|
528
|
-
def self_mute
|
529
|
-
voice_state_attribute(:self_mute)
|
530
|
-
end
|
531
|
-
|
532
|
-
# @return [true, false] whether this member has deafened themselves.
|
533
|
-
def self_deaf
|
534
|
-
voice_state_attribute(:self_deaf)
|
535
|
-
end
|
536
|
-
|
537
|
-
# @return [Channel] the voice channel this member is in.
|
538
|
-
def voice_channel
|
539
|
-
voice_state_attribute(:voice_channel)
|
540
|
-
end
|
541
|
-
|
542
|
-
alias_method :muted?, :mute
|
543
|
-
alias_method :deafened?, :deaf
|
544
|
-
alias_method :self_muted?, :self_mute
|
545
|
-
alias_method :self_deafened?, :self_deaf
|
546
|
-
|
547
|
-
include MemberAttributes
|
548
|
-
|
549
|
-
# @!visibility private
|
550
|
-
def initialize(data, server, bot)
|
551
|
-
@bot = bot
|
552
|
-
|
553
|
-
@user = bot.ensure_user(data['user'])
|
554
|
-
super @user # Initialize the delegate class
|
555
|
-
|
556
|
-
# Somehow, Discord doesn't send the server ID in the standard member format...
|
557
|
-
raise ArgumentError, 'Cannot create a member without any information about the server!' if server.nil? && data['guild_id'].nil?
|
558
|
-
@server = server || bot.server(data['guild_id'].to_i)
|
559
|
-
|
560
|
-
# Initialize the roles by getting the roles from the server one-by-one
|
561
|
-
update_roles(data['roles'])
|
562
|
-
|
563
|
-
@nick = data['nick']
|
564
|
-
@joined_at = data['joined_at'] ? Time.parse(data['joined_at']) : nil
|
565
|
-
end
|
566
|
-
|
567
|
-
# @return [true, false] whether this member is the server owner.
|
568
|
-
def owner?
|
569
|
-
@server.owner == self
|
570
|
-
end
|
571
|
-
|
572
|
-
# @param role [Role, Integer, #resolve_id] the role to check or its ID.
|
573
|
-
# @return [true, false] whether this member has the specified role.
|
574
|
-
def role?(role)
|
575
|
-
role = role.resolve_id
|
576
|
-
@roles.any? { |e| e.id == role }
|
577
|
-
end
|
578
|
-
|
579
|
-
# @see Member#set_roles
|
580
|
-
def roles=(role)
|
581
|
-
set_roles(role)
|
582
|
-
end
|
583
|
-
|
584
|
-
# Bulk sets a member's roles.
|
585
|
-
# @param role [Role, Array<Role>] The role(s) to set.
|
586
|
-
# @param reason [String] The reason the user's roles are being changed.
|
587
|
-
def set_roles(role, reason = nil)
|
588
|
-
role_ids = role_id_array(role)
|
589
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, roles: role_ids, reason: reason)
|
590
|
-
end
|
591
|
-
|
592
|
-
# Adds and removes roles from a member.
|
593
|
-
# @param add [Role, Array<Role>] The role(s) to add.
|
594
|
-
# @param remove [Role, Array<Role>] The role(s) to remove.
|
595
|
-
# @param reason [String] The reason the user's roles are being changed.
|
596
|
-
# @example Remove the 'Member' role from a user, and add the 'Muted' role to them.
|
597
|
-
# to_add = server.roles.find {|role| role.name == 'Muted'}
|
598
|
-
# to_remove = server.roles.find {|role| role.name == 'Member'}
|
599
|
-
# member.modify_roles(to_add, to_remove)
|
600
|
-
def modify_roles(add, remove, reason = nil)
|
601
|
-
add_role_ids = role_id_array(add)
|
602
|
-
remove_role_ids = role_id_array(remove)
|
603
|
-
old_role_ids = @roles.map(&:id)
|
604
|
-
new_role_ids = (old_role_ids - remove_role_ids + add_role_ids).uniq
|
605
|
-
|
606
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, roles: new_role_ids, reason: reason)
|
607
|
-
end
|
608
|
-
|
609
|
-
# Adds one or more roles to this member.
|
610
|
-
# @param role [Role, Array<Role, #resolve_id>, #resolve_id] The role(s) to add.
|
611
|
-
# @param reason [String] The reason the user's roles are being changed.
|
612
|
-
def add_role(role, reason = nil)
|
613
|
-
role_ids = role_id_array(role)
|
614
|
-
|
615
|
-
if role_ids.count == 1
|
616
|
-
API::Server.add_member_role(@bot.token, @server.id, @user.id, role_ids[0], reason)
|
617
|
-
else
|
618
|
-
old_role_ids = @roles.map(&:id)
|
619
|
-
new_role_ids = (old_role_ids + role_ids).uniq
|
620
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, roles: new_role_ids, reason: reason)
|
621
|
-
end
|
622
|
-
end
|
623
|
-
|
624
|
-
# Removes one or more roles from this member.
|
625
|
-
# @param role [Role, Array<Role>] The role(s) to remove.
|
626
|
-
# @param reason [String] The reason the user's roles are being changed.
|
627
|
-
def remove_role(role, reason = nil)
|
628
|
-
role_ids = role_id_array(role)
|
629
|
-
|
630
|
-
if role_ids.count == 1
|
631
|
-
API::Server.remove_member_role(@bot.token, @server.id, @user.id, role_ids[0], reason)
|
632
|
-
else
|
633
|
-
old_role_ids = @roles.map(&:id)
|
634
|
-
new_role_ids = old_role_ids.reject { |i| role_ids.include?(i) }
|
635
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, roles: new_role_ids, reason: reason)
|
636
|
-
end
|
637
|
-
end
|
638
|
-
|
639
|
-
# @return [Role] the highest role this member has.
|
640
|
-
def highest_role
|
641
|
-
@roles.sort_by(&:position).last
|
642
|
-
end
|
643
|
-
|
644
|
-
# @return [Role, nil] the role this member is being hoisted with.
|
645
|
-
def hoist_role
|
646
|
-
hoisted_roles = @roles.select(&:hoist)
|
647
|
-
return nil if hoisted_roles.empty?
|
648
|
-
hoisted_roles.sort_by(&:position).last
|
649
|
-
end
|
650
|
-
|
651
|
-
# @return [Role, nil] the role this member is basing their colour on.
|
652
|
-
def colour_role
|
653
|
-
coloured_roles = @roles.select { |v| v.colour.combined.nonzero? }
|
654
|
-
return nil if coloured_roles.empty?
|
655
|
-
coloured_roles.sort_by(&:position).last
|
656
|
-
end
|
657
|
-
alias_method :color_role, :colour_role
|
658
|
-
|
659
|
-
# @return [ColourRGB, nil] the colour this member has.
|
660
|
-
def colour
|
661
|
-
return nil unless colour_role
|
662
|
-
colour_role.color
|
663
|
-
end
|
664
|
-
alias_method :color, :colour
|
665
|
-
|
666
|
-
# Server deafens this member.
|
667
|
-
def server_deafen
|
668
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, deaf: true)
|
669
|
-
end
|
670
|
-
|
671
|
-
# Server undeafens this member.
|
672
|
-
def server_undeafen
|
673
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, deaf: false)
|
674
|
-
end
|
675
|
-
|
676
|
-
# Server mutes this member.
|
677
|
-
def server_mute
|
678
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, mute: true)
|
679
|
-
end
|
680
|
-
|
681
|
-
# Server unmutes this member.
|
682
|
-
def server_unmute
|
683
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, mute: false)
|
684
|
-
end
|
685
|
-
|
686
|
-
# @see Member#set_nick
|
687
|
-
def nick=(nick)
|
688
|
-
set_nick(nick)
|
689
|
-
end
|
690
|
-
|
691
|
-
alias_method :nickname=, :nick=
|
692
|
-
|
693
|
-
# Sets or resets this member's nickname. Requires the Change Nickname permission for the bot itself and Manage
|
694
|
-
# Nicknames for other users.
|
695
|
-
# @param nick [String, nil] The string to set the nickname to, or nil if it should be reset.
|
696
|
-
# @param reason [String] The reason the user's nickname is being changed.
|
697
|
-
def set_nick(nick, reason = nil)
|
698
|
-
# Discord uses the empty string to signify 'no nickname' so we convert nil into that
|
699
|
-
nick ||= ''
|
700
|
-
|
701
|
-
if @user.current_bot?
|
702
|
-
API::User.change_own_nickname(@bot.token, @server.id, nick, reason)
|
703
|
-
else
|
704
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, nick: nick, reason: nil)
|
705
|
-
end
|
706
|
-
end
|
707
|
-
|
708
|
-
alias_method :set_nickname, :set_nick
|
709
|
-
|
710
|
-
# @return [String] the name the user displays as (nickname if they have one, username otherwise)
|
711
|
-
def display_name
|
712
|
-
nickname || username
|
713
|
-
end
|
714
|
-
|
715
|
-
# Update this member's roles
|
716
|
-
# @note For internal use only.
|
717
|
-
# @!visibility private
|
718
|
-
def update_roles(role_ids)
|
719
|
-
@roles = []
|
720
|
-
role_ids.each do |id|
|
721
|
-
# It is posible for members to have roles that do not exist
|
722
|
-
# on the server any longer. See https://github.com/meew0/discordrb/issues/371
|
723
|
-
role = @server.role(id)
|
724
|
-
@roles << role if role
|
725
|
-
end
|
726
|
-
end
|
727
|
-
|
728
|
-
# Update this member's nick
|
729
|
-
# @note For internal use only.
|
730
|
-
# @!visibility private
|
731
|
-
def update_nick(nick)
|
732
|
-
@nick = nick
|
733
|
-
end
|
734
|
-
|
735
|
-
include PermissionCalculator
|
736
|
-
|
737
|
-
# Overwriting inspect for debug purposes
|
738
|
-
def inspect
|
739
|
-
"<Member user=#{@user.inspect} server=#{@server.inspect} joined_at=#{@joined_at} roles=#{@roles.inspect} voice_channel=#{@voice_channel.inspect} mute=#{@mute} deaf=#{@deaf} self_mute=#{@self_mute} self_deaf=#{@self_deaf}>"
|
740
|
-
end
|
741
|
-
|
742
|
-
private
|
743
|
-
|
744
|
-
# Utility method to get a list of role IDs from one role or an array of roles
|
745
|
-
def role_id_array(role)
|
746
|
-
if role.is_a? Array
|
747
|
-
role.map(&:resolve_id)
|
748
|
-
else
|
749
|
-
[role.resolve_id]
|
750
|
-
end
|
751
|
-
end
|
752
|
-
|
753
|
-
# Utility method to get data out of this member's voice state
|
754
|
-
def voice_state_attribute(name)
|
755
|
-
voice_state = @server.voice_states[@user.id]
|
756
|
-
voice_state.send name if voice_state
|
757
|
-
end
|
758
|
-
end
|
759
|
-
|
760
|
-
# Recipients are members on private channels - they exist for completeness purposes, but all
|
761
|
-
# the attributes will be empty.
|
762
|
-
class Recipient < DelegateClass(User)
|
763
|
-
include MemberAttributes
|
764
|
-
|
765
|
-
# @return [Channel] the private channel this recipient is the recipient of.
|
766
|
-
attr_reader :channel
|
767
|
-
|
768
|
-
# @!visibility private
|
769
|
-
def initialize(user, channel, bot)
|
770
|
-
@bot = bot
|
771
|
-
@channel = channel
|
772
|
-
raise ArgumentError, 'Tried to create a recipient for a public channel!' unless @channel.private?
|
773
|
-
|
774
|
-
@user = user
|
775
|
-
super @user
|
776
|
-
|
777
|
-
# Member attributes
|
778
|
-
@mute = @deaf = @self_mute = @self_deaf = false
|
779
|
-
@voice_channel = nil
|
780
|
-
@server = nil
|
781
|
-
@roles = []
|
782
|
-
@joined_at = @channel.creation_time
|
783
|
-
end
|
784
|
-
|
785
|
-
# Overwriting inspect for debug purposes
|
786
|
-
def inspect
|
787
|
-
"<Recipient user=#{@user.inspect} channel=#{@channel.inspect}>"
|
788
|
-
end
|
789
|
-
end
|
790
|
-
|
791
|
-
# This class is a special variant of User that represents the bot's user profile (things like own username and the avatar).
|
792
|
-
# It can be accessed using {Bot#profile}.
|
793
|
-
class Profile < User
|
794
|
-
def initialize(data, bot)
|
795
|
-
super(data, bot)
|
796
|
-
end
|
797
|
-
|
798
|
-
# Whether or not the user is the bot. The Profile can only ever be the bot user, so this always returns true.
|
799
|
-
# @return [true]
|
800
|
-
def current_bot?
|
801
|
-
true
|
802
|
-
end
|
803
|
-
|
804
|
-
# Sets the bot's username.
|
805
|
-
# @param username [String] The new username.
|
806
|
-
def username=(username)
|
807
|
-
update_profile_data(username: username)
|
808
|
-
end
|
809
|
-
|
810
|
-
alias_method :name=, :username=
|
811
|
-
|
812
|
-
# Changes the bot's avatar.
|
813
|
-
# @param avatar [String, #read] A JPG file to be used as the avatar, either
|
814
|
-
# something readable (e.g. File Object) or as a data URL.
|
815
|
-
def avatar=(avatar)
|
816
|
-
if avatar.respond_to? :read
|
817
|
-
# Set the file to binary mode if supported, so we don't get problems with Windows
|
818
|
-
avatar.binmode if avatar.respond_to?(:binmode)
|
819
|
-
|
820
|
-
avatar_string = 'data:image/jpg;base64,'
|
821
|
-
avatar_string += Base64.strict_encode64(avatar.read)
|
822
|
-
update_profile_data(avatar: avatar_string)
|
823
|
-
else
|
824
|
-
update_profile_data(avatar: avatar)
|
825
|
-
end
|
826
|
-
end
|
827
|
-
|
828
|
-
# Updates the cached profile data with the new one.
|
829
|
-
# @note For internal use only.
|
830
|
-
# @!visibility private
|
831
|
-
def update_data(new_data)
|
832
|
-
@username = new_data[:username] || @username
|
833
|
-
@avatar_id = new_data[:avatar_id] || @avatar_id
|
834
|
-
end
|
835
|
-
|
836
|
-
# Sets the user status setting to Online.
|
837
|
-
# @note Only usable on User accounts.
|
838
|
-
def online
|
839
|
-
update_profile_status_setting('online')
|
840
|
-
end
|
841
|
-
|
842
|
-
# Sets the user status setting to Idle.
|
843
|
-
# @note Only usable on User accounts.
|
844
|
-
def idle
|
845
|
-
update_profile_status_setting('idle')
|
846
|
-
end
|
847
|
-
|
848
|
-
# Sets the user status setting to Do Not Disturb.
|
849
|
-
# @note Only usable on User accounts.
|
850
|
-
def dnd
|
851
|
-
update_profile_status_setting('dnd')
|
852
|
-
end
|
853
|
-
|
854
|
-
alias_method(:busy, :dnd)
|
855
|
-
|
856
|
-
# Sets the user status setting to Invisible.
|
857
|
-
# @note Only usable on User accounts.
|
858
|
-
def invisible
|
859
|
-
update_profile_status_setting('invisible')
|
860
|
-
end
|
861
|
-
|
862
|
-
# The inspect method is overwritten to give more useful output
|
863
|
-
def inspect
|
864
|
-
"<Profile user=#{super}>"
|
865
|
-
end
|
866
|
-
|
867
|
-
private
|
868
|
-
|
869
|
-
# Internal handler for updating the user's status setting
|
870
|
-
def update_profile_status_setting(status)
|
871
|
-
API::User.change_status_setting(@bot.token, status)
|
872
|
-
end
|
873
|
-
|
874
|
-
def update_profile_data(new_data)
|
875
|
-
API::User.update_profile(@bot.token,
|
876
|
-
nil, nil,
|
877
|
-
new_data[:username] || @username,
|
878
|
-
new_data.key?(:avatar) ? new_data[:avatar] : @avatar_id)
|
879
|
-
update_data(new_data)
|
880
|
-
end
|
881
|
-
end
|
882
|
-
|
883
|
-
# A Discord role that contains permissions and applies to certain users
|
884
|
-
class Role
|
885
|
-
include IDObject
|
886
|
-
|
887
|
-
# @return [Permissions] this role's permissions.
|
888
|
-
attr_reader :permissions
|
889
|
-
|
890
|
-
# @return [String] this role's name ("new role" if it hasn't been changed)
|
891
|
-
attr_reader :name
|
892
|
-
|
893
|
-
# @return [Server] the server this role belongs to
|
894
|
-
attr_reader :server
|
895
|
-
|
896
|
-
# @return [true, false] whether or not this role should be displayed separately from other users
|
897
|
-
attr_reader :hoist
|
898
|
-
|
899
|
-
# @return [true, false] whether or not this role is managed by an integration or a bot
|
900
|
-
attr_reader :managed
|
901
|
-
alias_method :managed?, :managed
|
902
|
-
|
903
|
-
# @return [true, false] whether this role can be mentioned using a role mention
|
904
|
-
attr_reader :mentionable
|
905
|
-
alias_method :mentionable?, :mentionable
|
906
|
-
|
907
|
-
# @return [ColourRGB] the role colour
|
908
|
-
attr_reader :colour
|
909
|
-
alias_method :color, :colour
|
910
|
-
|
911
|
-
# @return [Integer] the position of this role in the hierarchy
|
912
|
-
attr_reader :position
|
913
|
-
|
914
|
-
# This class is used internally as a wrapper to a Role object that allows easy writing of permission data.
|
915
|
-
class RoleWriter
|
916
|
-
# @!visibility private
|
917
|
-
def initialize(role, token)
|
918
|
-
@role = role
|
919
|
-
@token = token
|
920
|
-
end
|
921
|
-
|
922
|
-
# Write the specified permission data to the role, without updating the permission cache
|
923
|
-
# @param bits [Integer] The packed permissions to write.
|
924
|
-
def write(bits)
|
925
|
-
@role.send(:packed=, bits, false)
|
926
|
-
end
|
927
|
-
|
928
|
-
# The inspect method is overridden, in this case to prevent the token being leaked
|
929
|
-
def inspect
|
930
|
-
"<RoleWriter role=#{@role} token=...>"
|
931
|
-
end
|
932
|
-
end
|
933
|
-
|
934
|
-
# @!visibility private
|
935
|
-
def initialize(data, bot, server = nil)
|
936
|
-
@bot = bot
|
937
|
-
@server = server
|
938
|
-
@permissions = Permissions.new(data['permissions'], RoleWriter.new(self, @bot.token))
|
939
|
-
@name = data['name']
|
940
|
-
@id = data['id'].to_i
|
941
|
-
|
942
|
-
@position = data['position']
|
943
|
-
|
944
|
-
@hoist = data['hoist']
|
945
|
-
@mentionable = data['mentionable']
|
946
|
-
@managed = data['managed']
|
947
|
-
|
948
|
-
@colour = ColourRGB.new(data['color'])
|
949
|
-
end
|
950
|
-
|
951
|
-
# @return [String] a string that will mention this role, if it is mentionable.
|
952
|
-
def mention
|
953
|
-
"<@&#{@id}>"
|
954
|
-
end
|
955
|
-
|
956
|
-
# @return [Array<Member>] an array of members who have this role.
|
957
|
-
# @note This requests a member chunk if it hasn't for the server before, which may be slow initially
|
958
|
-
def members
|
959
|
-
@server.members.select { |m| m.role? self }
|
960
|
-
end
|
961
|
-
|
962
|
-
alias_method :users, :members
|
963
|
-
|
964
|
-
# Updates the data cache from another Role object
|
965
|
-
# @note For internal use only
|
966
|
-
# @!visibility private
|
967
|
-
def update_from(other)
|
968
|
-
@permissions = other.permissions
|
969
|
-
@name = other.name
|
970
|
-
@hoist = other.hoist
|
971
|
-
@colour = other.colour
|
972
|
-
@position = other.position
|
973
|
-
@managed = other.managed
|
974
|
-
end
|
975
|
-
|
976
|
-
# Updates the data cache from a hash containing data
|
977
|
-
# @note For internal use only
|
978
|
-
# @!visibility private
|
979
|
-
def update_data(new_data)
|
980
|
-
@name = new_data[:name] || new_data['name'] || @name
|
981
|
-
@hoist = new_data['hoist'] unless new_data['hoist'].nil?
|
982
|
-
@hoist = new_data[:hoist] unless new_data[:hoist].nil?
|
983
|
-
@colour = new_data[:colour] || (new_data['color'] ? ColourRGB.new(new_data['color']) : @colour)
|
984
|
-
end
|
985
|
-
|
986
|
-
# Sets the role name to something new
|
987
|
-
# @param name [String] The name that should be set
|
988
|
-
def name=(name)
|
989
|
-
update_role_data(name: name)
|
990
|
-
end
|
991
|
-
|
992
|
-
# Changes whether or not this role is displayed at the top of the user list
|
993
|
-
# @param hoist [true, false] The value it should be changed to
|
994
|
-
def hoist=(hoist)
|
995
|
-
update_role_data(hoist: hoist)
|
996
|
-
end
|
997
|
-
|
998
|
-
# Changes whether or not this role can be mentioned
|
999
|
-
# @param mentionable [true, false] The value it should be changed to
|
1000
|
-
def mentionable=(mentionable)
|
1001
|
-
update_role_data(mentionable: mentionable)
|
1002
|
-
end
|
1003
|
-
|
1004
|
-
# Sets the role colour to something new
|
1005
|
-
# @param colour [ColourRGB] The new colour
|
1006
|
-
def colour=(colour)
|
1007
|
-
update_role_data(colour: colour)
|
1008
|
-
end
|
1009
|
-
|
1010
|
-
alias_method :color=, :colour=
|
1011
|
-
|
1012
|
-
# Changes this role's permissions to a fixed bitfield. This allows setting multiple permissions at once with just
|
1013
|
-
# one API call.
|
1014
|
-
#
|
1015
|
-
# Information on how this bitfield is structured can be found at
|
1016
|
-
# https://discordapp.com/developers/docs/topics/permissions.
|
1017
|
-
# @example Remove all permissions from a role
|
1018
|
-
# role.packed = 0
|
1019
|
-
# @param packed [Integer] A bitfield with the desired permissions value.
|
1020
|
-
# @param update_perms [true, false] Whether the internal data should also be updated. This should always be true
|
1021
|
-
# when calling externally.
|
1022
|
-
def packed=(packed, update_perms = true)
|
1023
|
-
update_role_data(permissions: packed)
|
1024
|
-
@permissions.bits = packed if update_perms
|
1025
|
-
end
|
1026
|
-
|
1027
|
-
# Moves this role above another role in the list.
|
1028
|
-
# @param other [Role, #resolve_id, nil] The role above which this role should be moved. If it is `nil`,
|
1029
|
-
# the role will be moved above the @everyone role.
|
1030
|
-
# @return [Integer] the new position of this role
|
1031
|
-
def sort_above(other = nil)
|
1032
|
-
other = @server.role(other.resolve_id) if other
|
1033
|
-
roles = @server.roles.sort_by(&:position)
|
1034
|
-
roles.delete_at(@position)
|
1035
|
-
|
1036
|
-
index = other ? roles.index { |role| role.id == other.id } + 1 : 1
|
1037
|
-
roles.insert(index, self)
|
1038
|
-
|
1039
|
-
updated_roles = roles.map.with_index { |role, position| { id: role.id, position: position } }
|
1040
|
-
@server.update_role_positions(updated_roles)
|
1041
|
-
index
|
1042
|
-
end
|
1043
|
-
|
1044
|
-
alias_method :move_above, :sort_above
|
1045
|
-
|
1046
|
-
# Deletes this role. This cannot be undone without recreating the role!
|
1047
|
-
# @param reason [String] the reason for this role's deletion
|
1048
|
-
def delete(reason = nil)
|
1049
|
-
API::Server.delete_role(@bot.token, @server.id, @id, reason)
|
1050
|
-
@server.delete_role(@id)
|
1051
|
-
end
|
1052
|
-
|
1053
|
-
# The inspect method is overwritten to give more useful output
|
1054
|
-
def inspect
|
1055
|
-
"<Role name=#{@name} permissions=#{@permissions.inspect} hoist=#{@hoist} colour=#{@colour.inspect} server=#{@server.inspect}>"
|
1056
|
-
end
|
1057
|
-
|
1058
|
-
private
|
1059
|
-
|
1060
|
-
def update_role_data(new_data)
|
1061
|
-
API::Server.update_role(@bot.token, @server.id, @id,
|
1062
|
-
new_data[:name] || @name,
|
1063
|
-
(new_data[:colour] || @colour).combined,
|
1064
|
-
new_data[:hoist].nil? ? @hoist : new_data[:hoist],
|
1065
|
-
new_data[:mentionable].nil? ? @mentionable : new_data[:mentionable],
|
1066
|
-
new_data[:permissions] || @permissions.bits)
|
1067
|
-
update_data(new_data)
|
1068
|
-
end
|
1069
|
-
end
|
1070
|
-
|
1071
|
-
# A channel referenced by an invite. It has less data than regular channels, so it's a separate class
|
1072
|
-
class InviteChannel
|
1073
|
-
include IDObject
|
1074
|
-
|
1075
|
-
# @return [String] this channel's name.
|
1076
|
-
attr_reader :name
|
1077
|
-
|
1078
|
-
# @return [Integer] this channel's type (0: text, 1: private, 2: voice, 3: group).
|
1079
|
-
attr_reader :type
|
1080
|
-
|
1081
|
-
# @!visibility private
|
1082
|
-
def initialize(data, bot)
|
1083
|
-
@bot = bot
|
1084
|
-
|
1085
|
-
@id = data['id'].to_i
|
1086
|
-
@name = data['name']
|
1087
|
-
@type = data['type']
|
1088
|
-
end
|
1089
|
-
end
|
1090
|
-
|
1091
|
-
# A server referenced to by an invite
|
1092
|
-
class InviteServer
|
1093
|
-
include IDObject
|
1094
|
-
|
1095
|
-
# @return [String] this server's name.
|
1096
|
-
attr_reader :name
|
1097
|
-
|
1098
|
-
# @return [String, nil] the hash of the server's invite splash screen (for partnered servers) or nil if none is
|
1099
|
-
# present
|
1100
|
-
attr_reader :splash_hash
|
1101
|
-
|
1102
|
-
# @!visibility private
|
1103
|
-
def initialize(data, bot)
|
1104
|
-
@bot = bot
|
1105
|
-
|
1106
|
-
@id = data['id'].to_i
|
1107
|
-
@name = data['name']
|
1108
|
-
@splash_hash = data['splash_hash']
|
1109
|
-
end
|
1110
|
-
end
|
1111
|
-
|
1112
|
-
# A Discord invite to a channel
|
1113
|
-
class Invite
|
1114
|
-
# @return [InviteChannel] the channel this invite references.
|
1115
|
-
attr_reader :channel
|
1116
|
-
|
1117
|
-
# @return [InviteServer] the server this invite references.
|
1118
|
-
attr_reader :server
|
1119
|
-
|
1120
|
-
# @return [Integer] the amount of uses left on this invite.
|
1121
|
-
attr_reader :uses
|
1122
|
-
alias_method :max_uses, :uses
|
1123
|
-
|
1124
|
-
# @return [User, nil] the user that made this invite. May also be nil if the user can't be determined.
|
1125
|
-
attr_reader :inviter
|
1126
|
-
alias_method :user, :inviter
|
1127
|
-
|
1128
|
-
# @return [true, false] whether or not this invite grants temporary membership. If someone joins a server with this invite, they will be removed from the server when they go offline unless they've received a role.
|
1129
|
-
attr_reader :temporary
|
1130
|
-
alias_method :temporary?, :temporary
|
1131
|
-
|
1132
|
-
# @return [true, false] whether this invite is still valid.
|
1133
|
-
attr_reader :revoked
|
1134
|
-
alias_method :revoked?, :revoked
|
1135
|
-
|
1136
|
-
# @return [String] this invite's code
|
1137
|
-
attr_reader :code
|
1138
|
-
|
1139
|
-
# @return [Integer, nil] the amount of members in the server. Will be nil if it has not been resolved.
|
1140
|
-
attr_reader :member_count
|
1141
|
-
alias_method :user_count, :member_count
|
1142
|
-
|
1143
|
-
# @return [Integer, nil] the amount of online members in the server. Will be nil if it has not been resolved.
|
1144
|
-
attr_reader :online_member_count
|
1145
|
-
alias_method :online_user_count, :online_member_count
|
1146
|
-
|
1147
|
-
# @return [Integer, nil] the invites max age before it expires, or nil if it's unknown. If the max age is 0, the invite will never expire unless it's deleted.
|
1148
|
-
attr_reader :max_age
|
1149
|
-
|
1150
|
-
# @return [Time, nil] when this invite was created, or nil if it's unknown
|
1151
|
-
attr_reader :created_at
|
1152
|
-
|
1153
|
-
# @!visibility private
|
1154
|
-
def initialize(data, bot)
|
1155
|
-
@bot = bot
|
1156
|
-
|
1157
|
-
@channel = InviteChannel.new(data['channel'], bot)
|
1158
|
-
@server = InviteServer.new(data['guild'], bot)
|
1159
|
-
@uses = data['uses']
|
1160
|
-
@inviter = data['inviter'] ? (@bot.user(data['inviter']['id'].to_i) || User.new(data['inviter'], bot)) : nil
|
1161
|
-
@temporary = data['temporary']
|
1162
|
-
@revoked = data['revoked']
|
1163
|
-
@online_member_count = data['approximate_presence_count']
|
1164
|
-
@member_count = data['approximate_member_count']
|
1165
|
-
@max_age = data['max_age']
|
1166
|
-
@created_at = data['created_at']
|
1167
|
-
|
1168
|
-
@code = data['code']
|
1169
|
-
end
|
1170
|
-
|
1171
|
-
# Code based comparison
|
1172
|
-
def ==(other)
|
1173
|
-
other.respond_to?(:code) ? (@code == other.code) : (@code == other)
|
1174
|
-
end
|
1175
|
-
|
1176
|
-
# Deletes this invite
|
1177
|
-
# @param reason [String] The reason the invite is being deleted.
|
1178
|
-
def delete(reason = nil)
|
1179
|
-
API::Invite.delete(@bot.token, @code, reason)
|
1180
|
-
end
|
1181
|
-
|
1182
|
-
alias_method :revoke, :delete
|
1183
|
-
|
1184
|
-
# The inspect method is overwritten to give more useful output
|
1185
|
-
def inspect
|
1186
|
-
"<Invite code=#{@code} channel=#{@channel} uses=#{@uses} temporary=#{@temporary} revoked=#{@revoked} created_at=#{@created_at} max_age=#{@max_age}>"
|
1187
|
-
end
|
1188
|
-
|
1189
|
-
# Creates an invite URL.
|
1190
|
-
def url
|
1191
|
-
"https://discord.gg/#{@code}"
|
1192
|
-
end
|
1193
|
-
end
|
1194
|
-
|
1195
|
-
# A permissions overwrite, when applied to channels describes additional
|
1196
|
-
# permissions a member needs to perform certain actions in context.
|
1197
|
-
class Overwrite
|
1198
|
-
# @return [Integer] id of the thing associated with this overwrite type
|
1199
|
-
attr_accessor :id
|
1200
|
-
|
1201
|
-
# @return [Symbol] either :role or :member
|
1202
|
-
attr_accessor :type
|
1203
|
-
|
1204
|
-
# @return [Permissions] allowed permissions for this overwrite type
|
1205
|
-
attr_accessor :allow
|
1206
|
-
|
1207
|
-
# @return [Permissions] denied permissions for this overwrite type
|
1208
|
-
attr_accessor :deny
|
1209
|
-
|
1210
|
-
# Creates a new Overwrite object
|
1211
|
-
# @example Create an overwrite for a role that can mention everyone, send TTS messages, but can't create instant invites
|
1212
|
-
# allow = Discordrb::Permissions.new
|
1213
|
-
# allow.can_mention_everyone = true
|
1214
|
-
# allow.can_send_tts_messages = true
|
1215
|
-
#
|
1216
|
-
# deny = Discordrb::Permissions.new
|
1217
|
-
# deny.can_create_instant_invite = true
|
1218
|
-
#
|
1219
|
-
# # Find some role by name
|
1220
|
-
# role = server.roles.find { |r| r.name == 'some role' }
|
1221
|
-
#
|
1222
|
-
# Overwrite.new(role, allow: allow, deny: deny)
|
1223
|
-
# @example Create an overwrite by ID and permissions bits
|
1224
|
-
# Overwrite.new(120571255635181568, type: 'member', allow: 1024, deny: 0)
|
1225
|
-
# @param object [Integer, #id] the ID or object this overwrite is for
|
1226
|
-
# @param type [String] the type of object this overwrite is for (only required if object is an Integer)
|
1227
|
-
# @param allow [Integer, Permissions] allowed permissions for this overwrite, by bits or a Permissions object
|
1228
|
-
# @param deny [Integer, Permissions] denied permissions for this overwrite, by bits or a Permissions object
|
1229
|
-
# @raise [ArgumentError] if type is not :member or :role
|
1230
|
-
def initialize(object = nil, type: nil, allow: 0, deny: 0)
|
1231
|
-
if type
|
1232
|
-
type = type.to_sym
|
1233
|
-
raise ArgumentError, 'Overwrite type must be :member or :role' unless (type != :member) || (type != :role)
|
1234
|
-
end
|
1235
|
-
|
1236
|
-
@id = object.respond_to?(:id) ? object.id : object
|
1237
|
-
|
1238
|
-
@type = if object.is_a?(User) || object.is_a?(Member) || object.is_a?(Recipient) || object.is_a?(Profile)
|
1239
|
-
:member
|
1240
|
-
elsif object.is_a? Role
|
1241
|
-
:role
|
1242
|
-
else
|
1243
|
-
type
|
1244
|
-
end
|
1245
|
-
|
1246
|
-
@allow = allow.is_a?(Permissions) ? allow : Permissions.new(allow)
|
1247
|
-
@deny = deny.is_a?(Permissions) ? deny : Permissions.new(deny)
|
1248
|
-
end
|
1249
|
-
|
1250
|
-
# Comparison by attributes [:id, :type, :allow, :deny]
|
1251
|
-
def ==(other)
|
1252
|
-
false unless other.is_a? Discordrb::Overwrite
|
1253
|
-
id == other.id &&
|
1254
|
-
type == other.type &&
|
1255
|
-
allow == other.allow &&
|
1256
|
-
deny == other.deny
|
1257
|
-
end
|
1258
|
-
|
1259
|
-
# @return [Overwrite] create an overwrite from a hash payload
|
1260
|
-
# @!visibility private
|
1261
|
-
def self.from_hash(data)
|
1262
|
-
new(
|
1263
|
-
data['id'].to_i,
|
1264
|
-
type: data['type'],
|
1265
|
-
allow: Permissions.new(data['allow']),
|
1266
|
-
deny: Permissions.new(data['deny'])
|
1267
|
-
)
|
1268
|
-
end
|
1269
|
-
|
1270
|
-
# @return [Overwrite] copies an overwrite from another Overwrite
|
1271
|
-
# @!visibility private
|
1272
|
-
def self.from_other(other)
|
1273
|
-
new(
|
1274
|
-
other.id,
|
1275
|
-
type: other.type,
|
1276
|
-
allow: Permissions.new(other.allow.bits),
|
1277
|
-
deny: Permissions.new(other.deny.bits)
|
1278
|
-
)
|
1279
|
-
end
|
1280
|
-
|
1281
|
-
# @return [Hash] hash representation of an overwrite
|
1282
|
-
# @!visibility private
|
1283
|
-
def to_hash
|
1284
|
-
{
|
1285
|
-
id: id,
|
1286
|
-
type: type,
|
1287
|
-
allow: allow.bits,
|
1288
|
-
deny: deny.bits
|
1289
|
-
}
|
1290
|
-
end
|
1291
|
-
end
|
1292
|
-
|
1293
|
-
# A Discord channel, including data like the topic
|
1294
|
-
class Channel
|
1295
|
-
include IDObject
|
1296
|
-
|
1297
|
-
# Map of channel types
|
1298
|
-
TYPES = {
|
1299
|
-
text: 0,
|
1300
|
-
dm: 1,
|
1301
|
-
voice: 2,
|
1302
|
-
group: 3,
|
1303
|
-
category: 4
|
1304
|
-
}.freeze
|
1305
|
-
|
1306
|
-
# @return [String] this channel's name.
|
1307
|
-
attr_reader :name
|
1308
|
-
|
1309
|
-
# @return [Server, nil] the server this channel is on. If this channel is a PM channel, it will be nil.
|
1310
|
-
attr_reader :server
|
1311
|
-
|
1312
|
-
# @return [Integer, nil] the ID of the parent channel, if this channel is inside a cateogry
|
1313
|
-
attr_reader :parent_id
|
1314
|
-
|
1315
|
-
# @return [Integer] the type of this channel (0: text, 1: private, 2: voice, 3: group)
|
1316
|
-
attr_reader :type
|
1317
|
-
|
1318
|
-
# @return [Integer, nil] the id of the owner of the group channel or nil if this is not a group channel.
|
1319
|
-
attr_reader :owner_id
|
1320
|
-
|
1321
|
-
# @return [Array<Recipient>, nil] the array of recipients of the private messages, or nil if this is not a Private channel
|
1322
|
-
attr_reader :recipients
|
1323
|
-
|
1324
|
-
# @return [String] the channel's topic
|
1325
|
-
attr_reader :topic
|
1326
|
-
|
1327
|
-
# @return [Integer] the bitrate (in bps) of the channel
|
1328
|
-
attr_reader :bitrate
|
1329
|
-
|
1330
|
-
# @return [Integer] the amount of users that can be in the channel. `0` means it is unlimited.
|
1331
|
-
attr_reader :user_limit
|
1332
|
-
alias_method :limit, :user_limit
|
1333
|
-
|
1334
|
-
# @return [Integer] the channel's position on the channel list
|
1335
|
-
attr_reader :position
|
1336
|
-
|
1337
|
-
# @return [true, false] if this channel is marked as nsfw
|
1338
|
-
attr_reader :nsfw
|
1339
|
-
alias_method :nsfw?, :nsfw
|
1340
|
-
|
1341
|
-
# @return [Integer] the amount of time (in seconds) users need to wait to send in between messages.
|
1342
|
-
attr_reader :rate_limit_per_user
|
1343
|
-
alias_method :slowmode_rate, :rate_limit_per_user
|
1344
|
-
|
1345
|
-
# @return [true, false] whether or not this channel is a PM or group channel.
|
1346
|
-
def private?
|
1347
|
-
pm? || group?
|
1348
|
-
end
|
1349
|
-
|
1350
|
-
# @return [String] a string that will mention the channel as a clickable link on Discord.
|
1351
|
-
def mention
|
1352
|
-
"<##{@id}>"
|
1353
|
-
end
|
1354
|
-
|
1355
|
-
# @return [Recipient, nil] the recipient of the private messages, or nil if this is not a PM channel
|
1356
|
-
def recipient
|
1357
|
-
@recipients.first if pm?
|
1358
|
-
end
|
1359
|
-
|
1360
|
-
# @!visibility private
|
1361
|
-
def initialize(data, bot, server = nil)
|
1362
|
-
@bot = bot
|
1363
|
-
# data is sometimes a Hash and other times an array of Hashes, you only want the last one if it's an array
|
1364
|
-
data = data[-1] if data.is_a?(Array)
|
1365
|
-
|
1366
|
-
@id = data['id'].to_i
|
1367
|
-
@type = data['type'] || 0
|
1368
|
-
@topic = data['topic']
|
1369
|
-
@bitrate = data['bitrate']
|
1370
|
-
@user_limit = data['user_limit']
|
1371
|
-
@position = data['position']
|
1372
|
-
@parent_id = data['parent_id'].to_i if data['parent_id']
|
1373
|
-
|
1374
|
-
if private?
|
1375
|
-
@recipients = []
|
1376
|
-
if data['recipients']
|
1377
|
-
data['recipients'].each do |recipient|
|
1378
|
-
recipient_user = bot.ensure_user(recipient)
|
1379
|
-
@recipients << Recipient.new(recipient_user, self, bot)
|
1380
|
-
end
|
1381
|
-
end
|
1382
|
-
if pm?
|
1383
|
-
@name = @recipients.first.username
|
1384
|
-
else
|
1385
|
-
@name = data['name']
|
1386
|
-
@owner_id = data['owner_id']
|
1387
|
-
end
|
1388
|
-
else
|
1389
|
-
@name = data['name']
|
1390
|
-
@server = if server
|
1391
|
-
server
|
1392
|
-
else
|
1393
|
-
bot.server(data['guild_id'].to_i)
|
1394
|
-
end
|
1395
|
-
end
|
1396
|
-
|
1397
|
-
@nsfw = data['nsfw'] || false || @name.start_with?('nsfw')
|
1398
|
-
@rate_limit_per_user = data['rate_limit_per_user'] || 0
|
1399
|
-
|
1400
|
-
process_permission_overwrites(data['permission_overwrites'])
|
1401
|
-
end
|
1402
|
-
|
1403
|
-
# @return [true, false] whether or not this channel is a text channel
|
1404
|
-
def text?
|
1405
|
-
@type.zero?
|
1406
|
-
end
|
1407
|
-
|
1408
|
-
# @return [true, false] whether or not this channel is a PM channel.
|
1409
|
-
def pm?
|
1410
|
-
@type == 1
|
1411
|
-
end
|
1412
|
-
|
1413
|
-
# @return [true, false] whether or not this channel is a voice channel.
|
1414
|
-
def voice?
|
1415
|
-
@type == 2
|
1416
|
-
end
|
1417
|
-
|
1418
|
-
# @return [true, false] whether or not this channel is a group channel.
|
1419
|
-
def group?
|
1420
|
-
@type == 3
|
1421
|
-
end
|
1422
|
-
|
1423
|
-
# @return [true, false]
|
1424
|
-
def category?
|
1425
|
-
@type == 4
|
1426
|
-
end
|
1427
|
-
|
1428
|
-
# @return [Channel, nil] the category channel, if this channel is in a category
|
1429
|
-
def category
|
1430
|
-
@bot.channel(@parent_id) if @parent_id
|
1431
|
-
end
|
1432
|
-
|
1433
|
-
alias_method :parent, :category
|
1434
|
-
|
1435
|
-
# Sets this channels parent category
|
1436
|
-
# @param channel [Channel, #resolve_id] the target category channel
|
1437
|
-
# @raise [ArgumentError] if the target channel isn't a category
|
1438
|
-
def category=(channel)
|
1439
|
-
channel = @bot.channel(channel)
|
1440
|
-
raise ArgumentError, 'Cannot set parent category to a channel that isn\'t a category' unless channel.category?
|
1441
|
-
update_channel_data(parent_id: channel.id)
|
1442
|
-
end
|
1443
|
-
|
1444
|
-
alias_method :parent=, :category=
|
1445
|
-
|
1446
|
-
# Sorts this channel's position to follow another channel.
|
1447
|
-
# @param other [Channel, #resolve_id, nil] The channel below which this channel should be sorted. If the given
|
1448
|
-
# channel is a category, this channel will be sorted at the top of that category. If it is `nil`, the channel will
|
1449
|
-
# be sorted at the top of the channel list.
|
1450
|
-
# @param lock_permissions [true, false] Whether the channel's permissions should be synced to the category's
|
1451
|
-
def sort_after(other = nil, lock_permissions = false)
|
1452
|
-
raise TypeError, 'other must be one of Channel, NilClass, or #resolve_id' unless other.is_a?(Channel) || other.nil? || other.respond_to?(:resolve_id)
|
1453
|
-
other = @bot.channel(other.resolve_id) if other
|
1454
|
-
|
1455
|
-
# Container for the API request payload
|
1456
|
-
move_argument = []
|
1457
|
-
|
1458
|
-
if other
|
1459
|
-
raise ArgumentError, 'Can only sort a channel after a channel of the same type!' unless other.category? || (@type == other.type)
|
1460
|
-
|
1461
|
-
raise ArgumentError, 'Can only sort a channel after a channel in the same server!' unless other.server == server
|
1462
|
-
|
1463
|
-
# Store `others` parent (or if `other` is a category itself)
|
1464
|
-
parent = if category? && other.category?
|
1465
|
-
# If we're sorting two categories, there is no new parent
|
1466
|
-
nil
|
1467
|
-
elsif other.category?
|
1468
|
-
# `other` is the category this channel will be moved into
|
1469
|
-
other
|
1470
|
-
else
|
1471
|
-
# `other`'s parent is the category this channel will be
|
1472
|
-
# moved into (if it exists)
|
1473
|
-
other.parent
|
1474
|
-
end
|
1475
|
-
end
|
1476
|
-
|
1477
|
-
# Collect and sort the IDs within the context (category or not) that we
|
1478
|
-
# need to form our payload with
|
1479
|
-
ids = if parent
|
1480
|
-
parent.children
|
1481
|
-
else
|
1482
|
-
@server.channels.reject(&:parent_id).select { |c| c.type == @type }
|
1483
|
-
end.sort_by(&:position).map(&:id)
|
1484
|
-
|
1485
|
-
# Move our channel ID after the target ID by deleting it,
|
1486
|
-
# getting the index of `other`, and inserting it after.
|
1487
|
-
ids.delete(@id) if ids.include?(@id)
|
1488
|
-
index = other ? (ids.index { |c| c == other.id } || -1) + 1 : 0
|
1489
|
-
ids.insert(index, @id)
|
1490
|
-
|
1491
|
-
# Generate `move_argument`, making the positions in order from how
|
1492
|
-
# we have sorted them in the above logic
|
1493
|
-
ids.each_with_index do |id, pos|
|
1494
|
-
# These keys are present in each element
|
1495
|
-
hash = { id: id, position: pos }
|
1496
|
-
|
1497
|
-
# Conditionally add `lock_permissions` and `parent_id` if we're
|
1498
|
-
# iterating past ourself
|
1499
|
-
if id == @id
|
1500
|
-
hash[:lock_permissions] = true if lock_permissions
|
1501
|
-
hash[:parent_id] = parent.nil? ? nil : parent.id
|
1502
|
-
end
|
1503
|
-
|
1504
|
-
# Add it to the stack
|
1505
|
-
move_argument << hash
|
1506
|
-
end
|
1507
|
-
|
1508
|
-
API::Server.update_channel_positions(@bot.token, @server.id, move_argument)
|
1509
|
-
end
|
1510
|
-
|
1511
|
-
# Sets whether this channel is NSFW
|
1512
|
-
# @param nsfw [true, false]
|
1513
|
-
# @raise [ArguementError] if value isn't one of true, false
|
1514
|
-
def nsfw=(nsfw)
|
1515
|
-
raise ArgumentError, 'nsfw value must be true or false' unless nsfw.is_a?(TrueClass) || nsfw.is_a?(FalseClass)
|
1516
|
-
update_channel_data(nsfw: nsfw)
|
1517
|
-
end
|
1518
|
-
|
1519
|
-
# This channel's permission overwrites
|
1520
|
-
# @overload permission_overwrites
|
1521
|
-
# The overwrites represented as a hash of role/user ID
|
1522
|
-
# to an Overwrite object
|
1523
|
-
# @return [Hash<Integer => Overwrite>] the channel's permission overwrites
|
1524
|
-
# @overload permission_overwrites(type)
|
1525
|
-
# Return an array of a certain type of overwrite
|
1526
|
-
# @param type [Symbol] the kind of overwrite to return
|
1527
|
-
# @return [Array<Overwrite>]
|
1528
|
-
def permission_overwrites(type = nil)
|
1529
|
-
return @permission_overwrites unless type
|
1530
|
-
@permission_overwrites.values.select { |e| e.type == type }
|
1531
|
-
end
|
1532
|
-
|
1533
|
-
alias_method :overwrites, :permission_overwrites
|
1534
|
-
|
1535
|
-
# Bulk sets this channels permission overwrites
|
1536
|
-
# @param overwrites [Array<Overwrite>]
|
1537
|
-
def permission_overwrites=(overwrites)
|
1538
|
-
update_channel_data(permission_overwrites: overwrites)
|
1539
|
-
end
|
1540
|
-
|
1541
|
-
# Sets the amount of time (in seconds) users have to wait in between sending messages.
|
1542
|
-
# @param rate [Integer]
|
1543
|
-
# @raise [ArgumentError] if value isn't between 0 and 120
|
1544
|
-
def rate_limit_per_user=(rate)
|
1545
|
-
raise ArgumentError, 'rate_limit_per_user must be between 0 and 120' unless rate.between?(0, 120)
|
1546
|
-
update_channel_data(rate_limit_per_user: rate)
|
1547
|
-
end
|
1548
|
-
|
1549
|
-
alias_method :slowmode_rate=, :rate_limit_per_user=
|
1550
|
-
|
1551
|
-
# Syncs this channels overwrites with its parent category
|
1552
|
-
# @raise [RuntimeError] if this channel is not in a category
|
1553
|
-
def sync_overwrites
|
1554
|
-
raise 'Cannot sync overwrites on a channel with no parent category' unless parent
|
1555
|
-
self.permission_overwrites = parent.permission_overwrites
|
1556
|
-
end
|
1557
|
-
|
1558
|
-
alias_method :sync, :sync_overwrites
|
1559
|
-
|
1560
|
-
# @return [true, false, nil] whether this channels permissions match the permission overwrites of the category that it's in, or nil if it is not in a category
|
1561
|
-
def synchronized?
|
1562
|
-
return unless parent
|
1563
|
-
permission_overwrites == parent.permission_overwrites
|
1564
|
-
end
|
1565
|
-
|
1566
|
-
alias_method :synced?, :synchronized?
|
1567
|
-
|
1568
|
-
# Returns the children of this channel, if it is a category. Otherwise returns an empty array.
|
1569
|
-
# @return [Array<Channel>]
|
1570
|
-
def children
|
1571
|
-
return [] unless category?
|
1572
|
-
server.channels.select { |c| c.parent_id == id }
|
1573
|
-
end
|
1574
|
-
|
1575
|
-
alias_method :channels, :children
|
1576
|
-
|
1577
|
-
# Returns the text channels in this category, if it is a category channel. Otherwise returns an empty array.
|
1578
|
-
# @return [Array<Channel>]
|
1579
|
-
def text_channels
|
1580
|
-
children.select(&:text?)
|
1581
|
-
end
|
1582
|
-
|
1583
|
-
# Returns the voice channels in this category, if it is a category channel. Otherwise returns an empty array.
|
1584
|
-
# @return [Array<Channel>]
|
1585
|
-
def voice_channels
|
1586
|
-
children.select(&:voice?)
|
1587
|
-
end
|
1588
|
-
|
1589
|
-
# @return [Overwrite] any member-type permission overwrites on this channel
|
1590
|
-
def member_overwrites
|
1591
|
-
permission_overwrites :member
|
1592
|
-
end
|
1593
|
-
|
1594
|
-
# @return [Overwrite] any role-type permission overwrites on this channel
|
1595
|
-
def role_overwrites
|
1596
|
-
permission_overwrites :role
|
1597
|
-
end
|
1598
|
-
|
1599
|
-
# @return [true, false] whether or not this channel is the default channel
|
1600
|
-
def default_channel?
|
1601
|
-
server.default_channel == self
|
1602
|
-
end
|
1603
|
-
|
1604
|
-
alias_method :default?, :default_channel?
|
1605
|
-
|
1606
|
-
# @return [true, false] whether or not this channel has slowmode enabled
|
1607
|
-
def slowmode?
|
1608
|
-
@rate_limit_per_user != 0
|
1609
|
-
end
|
1610
|
-
|
1611
|
-
# Sends a message to this channel.
|
1612
|
-
# @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
|
1613
|
-
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
|
1614
|
-
# @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
|
1615
|
-
# @return [Message] the message that was sent.
|
1616
|
-
def send_message(content, tts = false, embed = nil)
|
1617
|
-
@bot.send_message(@id, content, tts, embed)
|
1618
|
-
end
|
1619
|
-
|
1620
|
-
alias_method :send, :send_message
|
1621
|
-
|
1622
|
-
# Sends a temporary message to this channel.
|
1623
|
-
# @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
|
1624
|
-
# @param timeout [Float] The amount of time in seconds after which the message sent will be deleted.
|
1625
|
-
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
|
1626
|
-
# @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
|
1627
|
-
def send_temporary_message(content, timeout, tts = false, embed = nil)
|
1628
|
-
@bot.send_temporary_message(@id, content, timeout, tts, embed)
|
1629
|
-
end
|
1630
|
-
|
1631
|
-
# Convenience method to send a message with an embed.
|
1632
|
-
# @example Send a message with an embed
|
1633
|
-
# channel.send_embed do |embed|
|
1634
|
-
# embed.title = 'The Ruby logo'
|
1635
|
-
# embed.image = Discordrb::Webhooks::EmbedImage.new(url: 'https://www.ruby-lang.org/images/header-ruby-logo.png')
|
1636
|
-
# end
|
1637
|
-
# @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.
|
1638
|
-
# @param embed [Discordrb::Webhooks::Embed, nil] The embed to start the building process with, or nil if one should be created anew.
|
1639
|
-
# @yield [embed] Yields the embed to allow for easy building inside a block.
|
1640
|
-
# @yieldparam embed [Discordrb::Webhooks::Embed] The embed from the parameters, or a new one.
|
1641
|
-
# @return [Message] The resulting message.
|
1642
|
-
def send_embed(message = '', embed = nil)
|
1643
|
-
embed ||= Discordrb::Webhooks::Embed.new
|
1644
|
-
yield(embed) if block_given?
|
1645
|
-
send_message(message, false, embed)
|
1646
|
-
end
|
1647
|
-
|
1648
|
-
# Sends multiple messages to a channel
|
1649
|
-
# @param content [Array<String>] The messages to send.
|
1650
|
-
def send_multiple(content)
|
1651
|
-
content.each { |e| send_message(e) }
|
1652
|
-
end
|
1653
|
-
|
1654
|
-
# Splits a message into chunks whose length is at most the Discord character limit, then sends them individually.
|
1655
|
-
# Useful for sending long messages, but be wary of rate limits!
|
1656
|
-
def split_send(content)
|
1657
|
-
send_multiple(Discordrb.split_message(content))
|
1658
|
-
end
|
1659
|
-
|
1660
|
-
# Sends a file to this channel. If it is an image, it will be embedded.
|
1661
|
-
# @param file [File] The file to send. There's no clear size limit for this, you'll have to attempt it for yourself (most non-image files are fine, large images may fail to embed)
|
1662
|
-
# @param caption [string] The caption for the file.
|
1663
|
-
# @param tts [true, false] Whether or not this file's caption should be sent using Discord text-to-speech.
|
1664
|
-
# @example Send a file from disk
|
1665
|
-
# channel.send_file(File.open('rubytaco.png', 'r'))
|
1666
|
-
def send_file(file, caption: nil, tts: false)
|
1667
|
-
@bot.send_file(@id, file, caption: caption, tts: tts)
|
1668
|
-
end
|
1669
|
-
|
1670
|
-
# Deletes a message on this channel. Mostly useful in case a message needs to be deleted when only the ID is known
|
1671
|
-
# @param message [Message, String, Integer, #resolve_id] The message that should be deleted.
|
1672
|
-
def delete_message(message)
|
1673
|
-
API::Channel.delete_message(@bot.token, @id, message.resolve_id)
|
1674
|
-
end
|
1675
|
-
|
1676
|
-
# Permanently deletes this channel
|
1677
|
-
# @param reason [String] The reason the for the channel deletion.
|
1678
|
-
def delete(reason = nil)
|
1679
|
-
API::Channel.delete(@bot.token, @id, reason)
|
1680
|
-
end
|
1681
|
-
|
1682
|
-
# Sets this channel's name. The name must be alphanumeric with dashes, unless this is a voice channel (then there are no limitations)
|
1683
|
-
# @param name [String] The new name.
|
1684
|
-
def name=(name)
|
1685
|
-
update_channel_data(name: name)
|
1686
|
-
end
|
1687
|
-
|
1688
|
-
# Sets this channel's topic.
|
1689
|
-
# @param topic [String] The new topic.
|
1690
|
-
def topic=(topic)
|
1691
|
-
raise 'Tried to set topic on voice channel' if voice?
|
1692
|
-
update_channel_data(topic: topic)
|
1693
|
-
end
|
1694
|
-
|
1695
|
-
# Sets this channel's bitrate.
|
1696
|
-
# @param bitrate [Integer] The new bitrate (in bps). Number has to be between 8000-96000 (128000 for VIP servers)
|
1697
|
-
def bitrate=(bitrate)
|
1698
|
-
raise 'Tried to set bitrate on text channel' if text?
|
1699
|
-
update_channel_data(bitrate: bitrate)
|
1700
|
-
end
|
1701
|
-
|
1702
|
-
# Sets this channel's user limit.
|
1703
|
-
# @param limit [Integer] The new user limit. `0` for unlimited, has to be a number between 0-99
|
1704
|
-
def user_limit=(limit)
|
1705
|
-
raise 'Tried to set user_limit on text channel' if text?
|
1706
|
-
update_channel_data(user_limit: limit)
|
1707
|
-
end
|
1708
|
-
|
1709
|
-
alias_method :limit=, :user_limit=
|
1710
|
-
|
1711
|
-
# Sets this channel's position in the list.
|
1712
|
-
# @param position [Integer] The new position.
|
1713
|
-
def position=(position)
|
1714
|
-
update_channel_data(position: position)
|
1715
|
-
end
|
1716
|
-
|
1717
|
-
# Defines a permission overwrite for this channel that sets the specified thing to the specified allow and deny
|
1718
|
-
# permission sets, or change an existing one.
|
1719
|
-
# @overload define_overwrite(overwrite)
|
1720
|
-
# @param thing [Overwrite] an Overwrite object to apply to this channel
|
1721
|
-
# @param reason [String] The reason the for defining the overwrite.
|
1722
|
-
# @overload define_overwrite(thing, allow, deny)
|
1723
|
-
# @param thing [User, Role] What to define an overwrite for.
|
1724
|
-
# @param allow [#bits, Permissions, Integer] The permission sets that should receive an `allow` override (i.e. a
|
1725
|
-
# green checkmark on Discord)
|
1726
|
-
# @param deny [#bits, Permissions, Integer] The permission sets that should receive a `deny` override (i.e. a red
|
1727
|
-
# cross on Discord)
|
1728
|
-
# @param reason [String] The reason the for defining the overwrite.
|
1729
|
-
# @example Define a permission overwrite for a user that can then mention everyone and use TTS, but not create any invites
|
1730
|
-
# allow = Discordrb::Permissions.new
|
1731
|
-
# allow.can_mention_everyone = true
|
1732
|
-
# allow.can_send_tts_messages = true
|
1733
|
-
#
|
1734
|
-
# deny = Discordrb::Permissions.new
|
1735
|
-
# deny.can_create_instant_invite = true
|
1736
|
-
#
|
1737
|
-
# channel.define_overwrite(user, allow, deny)
|
1738
|
-
def define_overwrite(thing, allow = 0, deny = 0, reason: nil)
|
1739
|
-
unless thing.is_a? Overwrite
|
1740
|
-
allow_bits = allow.respond_to?(:bits) ? allow.bits : allow
|
1741
|
-
deny_bits = deny.respond_to?(:bits) ? deny.bits : deny
|
1742
|
-
|
1743
|
-
thing = Overwrite.new thing, allow: allow_bits, deny: deny_bits
|
1744
|
-
end
|
1745
|
-
|
1746
|
-
API::Channel.update_permission(@bot.token, @id, thing.id, thing.allow.bits, thing.deny.bits, thing.type, reason)
|
1747
|
-
end
|
1748
|
-
|
1749
|
-
# Deletes a permission overwrite for this channel
|
1750
|
-
# @param target [Member, User, Role, Profile, Recipient, #resolve_id] What permission overwrite to delete
|
1751
|
-
# @param reason [String] The reason the for the overwrite deletion.
|
1752
|
-
def delete_overwrite(target, reason = nil)
|
1753
|
-
raise 'Tried deleting a overwrite for an invalid target' unless target.is_a?(Member) || target.is_a?(User) || target.is_a?(Role) || target.is_a?(Profile) || target.is_a?(Recipient) || target.respond_to?(:resolve_id)
|
1754
|
-
|
1755
|
-
API::Channel.delete_permission(@bot.token, @id, target.resolve_id, reason)
|
1756
|
-
end
|
1757
|
-
|
1758
|
-
# Updates the cached data from another channel.
|
1759
|
-
# @note For internal use only
|
1760
|
-
# @!visibility private
|
1761
|
-
def update_from(other)
|
1762
|
-
@topic = other.topic
|
1763
|
-
@name = other.name
|
1764
|
-
@position = other.position
|
1765
|
-
@topic = other.topic
|
1766
|
-
@recipients = other.recipients
|
1767
|
-
@bitrate = other.bitrate
|
1768
|
-
@user_limit = other.user_limit
|
1769
|
-
@permission_overwrites = other.permission_overwrites
|
1770
|
-
@nsfw = other.nsfw
|
1771
|
-
@parent_id = other.parent_id
|
1772
|
-
@rate_limit_per_user = other.rate_limit_per_user
|
1773
|
-
end
|
1774
|
-
|
1775
|
-
# The list of users currently in this channel. For a voice channel, it will return all the members currently
|
1776
|
-
# in that channel. For a text channel, it will return all online members that have permission to read it.
|
1777
|
-
# @return [Array<Member>] the users in this channel
|
1778
|
-
def users
|
1779
|
-
if text?
|
1780
|
-
@server.online_members(include_idle: true).select { |u| u.can_read_messages? self }
|
1781
|
-
elsif voice?
|
1782
|
-
@server.voice_states.map { |id, voice_state| @server.member(id) if !voice_state.voice_channel.nil? && voice_state.voice_channel.id == @id }.compact
|
1783
|
-
end
|
1784
|
-
end
|
1785
|
-
|
1786
|
-
# Retrieves some of this channel's message history.
|
1787
|
-
# @param amount [Integer] How many messages to retrieve. This must be less than or equal to 100, if it is higher
|
1788
|
-
# than 100 it will be treated as 100 on Discord's side.
|
1789
|
-
# @param before_id [Integer] The ID of the most recent message the retrieval should start at, or nil if it should
|
1790
|
-
# start at the current message.
|
1791
|
-
# @param after_id [Integer] The ID of the oldest message the retrieval should start at, or nil if it should start
|
1792
|
-
# as soon as possible with the specified amount.
|
1793
|
-
# @param around_id [Integer] The ID of the message retrieval should start from, reading in both directions
|
1794
|
-
# @example Count the number of messages in the last 50 messages that contain the letter 'e'.
|
1795
|
-
# message_count = channel.history(50).count {|message| message.content.include? "e"}
|
1796
|
-
# @example Get the last 10 messages before the provided message.
|
1797
|
-
# last_ten_messages = channel.history(10, message.id)
|
1798
|
-
# @return [Array<Message>] the retrieved messages.
|
1799
|
-
def history(amount, before_id = nil, after_id = nil, around_id = nil)
|
1800
|
-
logs = API::Channel.messages(@bot.token, @id, amount, before_id, after_id, around_id)
|
1801
|
-
JSON.parse(logs).map { |message| Message.new(message, @bot) }
|
1802
|
-
end
|
1803
|
-
|
1804
|
-
# Retrieves message history, but only message IDs for use with prune.
|
1805
|
-
# @note For internal use only
|
1806
|
-
# @!visibility private
|
1807
|
-
def history_ids(amount, before_id = nil, after_id = nil, around_id = nil)
|
1808
|
-
logs = API::Channel.messages(@bot.token, @id, amount, before_id, after_id, around_id)
|
1809
|
-
JSON.parse(logs).map { |message| message['id'].to_i }
|
1810
|
-
end
|
1811
|
-
|
1812
|
-
# Returns a single message from this channel's history by ID.
|
1813
|
-
# @param message_id [Integer] The ID of the message to retrieve.
|
1814
|
-
# @return [Message, nil] the retrieved message, or `nil` if it couldn't be found.
|
1815
|
-
def load_message(message_id)
|
1816
|
-
response = API::Channel.message(@bot.token, @id, message_id)
|
1817
|
-
return Message.new(JSON.parse(response), @bot)
|
1818
|
-
rescue RestClient::ResourceNotFound
|
1819
|
-
return nil
|
1820
|
-
end
|
1821
|
-
|
1822
|
-
alias_method :message, :load_message
|
1823
|
-
|
1824
|
-
# Requests all pinned messages in a channel.
|
1825
|
-
# @return [Array<Message>] the received messages.
|
1826
|
-
def pins
|
1827
|
-
msgs = API::Channel.pinned_messages(@bot.token, @id)
|
1828
|
-
JSON.parse(msgs).map { |msg| Message.new(msg, @bot) }
|
1829
|
-
end
|
1830
|
-
|
1831
|
-
# Delete the last N messages on this channel.
|
1832
|
-
# @param amount [Integer] The amount of message history to consider for pruning. Must be a value between 2 and 100 (Discord limitation)
|
1833
|
-
# @param strict [true, false] Whether an error should be raised when a message is reached that is too old to be bulk
|
1834
|
-
# deleted. If this is false only a warning message will be output to the console.
|
1835
|
-
# @raise [ArgumentError] if the amount of messages is not a value between 2 and 100
|
1836
|
-
# @yield [message] Yields each message in this channels history for filtering the messages to delete
|
1837
|
-
# @example Pruning messages from a specific user ID
|
1838
|
-
# channel.prune(100) { |m| m.author.id == 83283213010599936 }
|
1839
|
-
# @return [Integer] The amount of messages that were successfully deleted
|
1840
|
-
def prune(amount, strict = false, &block)
|
1841
|
-
raise ArgumentError, 'Can only delete between 1 and 100 messages!' unless amount.between?(1, 100)
|
1842
|
-
|
1843
|
-
messages =
|
1844
|
-
if block_given?
|
1845
|
-
history(amount).select(&block).map(&:id)
|
1846
|
-
else
|
1847
|
-
history_ids(amount)
|
1848
|
-
end
|
1849
|
-
|
1850
|
-
case messages.size
|
1851
|
-
when 0
|
1852
|
-
0
|
1853
|
-
when 1
|
1854
|
-
API::Channel.delete_message(@bot.token, @id, messages.first)
|
1855
|
-
1
|
1856
|
-
else
|
1857
|
-
bulk_delete(messages, strict)
|
1858
|
-
end
|
1859
|
-
end
|
1860
|
-
|
1861
|
-
# Deletes a collection of messages
|
1862
|
-
# @param messages [Array<Message, Integer, #resolve_id>] the messages (or message IDs) to delete. Total must be an amount between 2 and 100 (Discord limitation)
|
1863
|
-
# @param strict [true, false] Whether an error should be raised when a message is reached that is too old to be bulk
|
1864
|
-
# deleted. If this is false only a warning message will be output to the console.
|
1865
|
-
# @raise [ArgumentError] if the amount of messages is not a value between 2 and 100
|
1866
|
-
# @return [Integer] The amount of messages that were successfully deleted
|
1867
|
-
def delete_messages(messages, strict = false)
|
1868
|
-
raise ArgumentError, 'Can only delete between 2 and 100 messages!' unless messages.count.between?(2, 100)
|
1869
|
-
|
1870
|
-
messages.map!(&:resolve_id)
|
1871
|
-
bulk_delete(messages, strict)
|
1872
|
-
end
|
1873
|
-
|
1874
|
-
# Updates the cached permission overwrites
|
1875
|
-
# @note For internal use only
|
1876
|
-
# @!visibility private
|
1877
|
-
def update_overwrites(overwrites)
|
1878
|
-
@permission_overwrites = overwrites
|
1879
|
-
end
|
1880
|
-
|
1881
|
-
# Add an {Await} for a message in this channel. This is identical in functionality to adding a
|
1882
|
-
# {Discordrb::Events::MessageEvent} await with the `in` attribute as this channel.
|
1883
|
-
# @see Bot#add_await
|
1884
|
-
# @deprecated Will be changed to blocking behavior in v4.0. Use {#await!} instead.
|
1885
|
-
def await(key, attributes = {}, &block)
|
1886
|
-
@bot.add_await(key, Discordrb::Events::MessageEvent, { in: @id }.merge(attributes), &block)
|
1887
|
-
end
|
1888
|
-
|
1889
|
-
# Add a blocking {Await} for a message in this channel. This is identical in functionality to adding a
|
1890
|
-
# {Discordrb::Events::MessageEvent} await with the `in` attribute as this channel.
|
1891
|
-
# @see Bot#add_await!
|
1892
|
-
def await!(attributes = {})
|
1893
|
-
@bot.add_await!(Discordrb::Events::MessageEvent, { in: @id }.merge(attributes))
|
1894
|
-
end
|
1895
|
-
|
1896
|
-
# Creates a new invite to this channel.
|
1897
|
-
# @param max_age [Integer] How many seconds this invite should last.
|
1898
|
-
# @param max_uses [Integer] How many times this invite should be able to be used.
|
1899
|
-
# @param temporary [true, false] Whether membership should be temporary (kicked after going offline).
|
1900
|
-
# @param unique [true, false] If true, Discord will always send a unique invite instead of possibly re-using a similar one
|
1901
|
-
# @param reason [String] The reason the for the creation of this invite.
|
1902
|
-
# @return [Invite] the created invite.
|
1903
|
-
def make_invite(max_age = 0, max_uses = 0, temporary = false, unique = false, reason = nil)
|
1904
|
-
response = API::Channel.create_invite(@bot.token, @id, max_age, max_uses, temporary, unique, reason)
|
1905
|
-
Invite.new(JSON.parse(response), @bot)
|
1906
|
-
end
|
1907
|
-
|
1908
|
-
alias_method :invite, :make_invite
|
1909
|
-
|
1910
|
-
# Starts typing, which displays the typing indicator on the client for five seconds.
|
1911
|
-
# If you want to keep typing you'll have to resend this every five seconds. (An abstraction
|
1912
|
-
# for this will eventually be coming)
|
1913
|
-
# @example Send a typing indicator for the bot in a given channel.
|
1914
|
-
# channel.start_typing()
|
1915
|
-
def start_typing
|
1916
|
-
API::Channel.start_typing(@bot.token, @id)
|
1917
|
-
end
|
1918
|
-
|
1919
|
-
# Creates a Group channel
|
1920
|
-
# @param user_ids [Array<Integer>] Array of user IDs to add to the new group channel (Excluding
|
1921
|
-
# the recipient of the PM channel).
|
1922
|
-
# @return [Channel] the created channel.
|
1923
|
-
def create_group(user_ids)
|
1924
|
-
raise 'Attempted to create group channel on a non-pm channel!' unless pm?
|
1925
|
-
response = API::Channel.create_group(@bot.token, @id, user_ids.shift)
|
1926
|
-
channel = Channel.new(JSON.parse(response), @bot)
|
1927
|
-
channel.add_group_users(user_ids)
|
1928
|
-
end
|
1929
|
-
|
1930
|
-
# Adds a user to a group channel.
|
1931
|
-
# @param user_ids [Array<#resolve_id>, #resolve_id] User ID or array of user IDs to add to the group channel.
|
1932
|
-
# @return [Channel] the group channel.
|
1933
|
-
def add_group_users(user_ids)
|
1934
|
-
raise 'Attempted to add a user to a non-group channel!' unless group?
|
1935
|
-
user_ids = [user_ids] unless user_ids.is_a? Array
|
1936
|
-
user_ids.each do |user_id|
|
1937
|
-
API::Channel.add_group_user(@bot.token, @id, user_id.resolve_id)
|
1938
|
-
end
|
1939
|
-
self
|
1940
|
-
end
|
1941
|
-
|
1942
|
-
alias_method :add_group_user, :add_group_users
|
1943
|
-
|
1944
|
-
# Removes a user from a group channel.
|
1945
|
-
# @param user_ids [Array<#resolve_id>, #resolve_id] User ID or array of user IDs to remove from the group channel.
|
1946
|
-
# @return [Channel] the group channel.
|
1947
|
-
def remove_group_users(user_ids)
|
1948
|
-
raise 'Attempted to remove a user from a non-group channel!' unless group?
|
1949
|
-
user_ids = [user_ids] unless user_ids.is_a? Array
|
1950
|
-
user_ids.each do |user_id|
|
1951
|
-
API::Channel.remove_group_user(@bot.token, @id, user_id.resolve_id)
|
1952
|
-
end
|
1953
|
-
self
|
1954
|
-
end
|
1955
|
-
|
1956
|
-
alias_method :remove_group_user, :remove_group_users
|
1957
|
-
|
1958
|
-
# Leaves the group.
|
1959
|
-
def leave_group
|
1960
|
-
raise 'Attempted to leave a non-group channel!' unless group?
|
1961
|
-
API::Channel.leave_group(@bot.token, @id)
|
1962
|
-
end
|
1963
|
-
|
1964
|
-
alias_method :leave, :leave_group
|
1965
|
-
|
1966
|
-
# Requests a list of Webhooks on the channel.
|
1967
|
-
# @return [Array<Webhook>] webhooks on the channel.
|
1968
|
-
def webhooks
|
1969
|
-
raise 'Tried to request webhooks from a non-server channel' unless server
|
1970
|
-
webhooks = JSON.parse(API::Channel.webhooks(@bot.token, @id))
|
1971
|
-
webhooks.map { |webhook_data| Webhook.new(webhook_data, @bot) }
|
1972
|
-
end
|
1973
|
-
|
1974
|
-
# Requests a list of Invites to the channel.
|
1975
|
-
# @return [Array<Invite>] invites to the channel.
|
1976
|
-
def invites
|
1977
|
-
raise 'Tried to request invites from a non-server channel' unless server
|
1978
|
-
invites = JSON.parse(API::Channel.invites(@bot.token, @id))
|
1979
|
-
invites.map { |invite_data| Invite.new(invite_data, @bot) }
|
1980
|
-
end
|
1981
|
-
|
1982
|
-
# The default `inspect` method is overwritten to give more useful output.
|
1983
|
-
def inspect
|
1984
|
-
"<Channel name=#{@name} id=#{@id} topic=\"#{@topic}\" type=#{@type} position=#{@position} server=#{@server}>"
|
1985
|
-
end
|
1986
|
-
|
1987
|
-
# Adds a recipient to a group channel.
|
1988
|
-
# @param recipient [Recipient] the recipient to add to the group
|
1989
|
-
# @raise [ArgumentError] if tried to add a non-recipient
|
1990
|
-
# @note For internal use only
|
1991
|
-
# @!visibility private
|
1992
|
-
def add_recipient(recipient)
|
1993
|
-
raise 'Tried to add recipient to a non-group channel' unless group?
|
1994
|
-
raise ArgumentError, 'Tried to add a non-recipient to a group' unless recipient.is_a?(Recipient)
|
1995
|
-
@recipients << recipient
|
1996
|
-
end
|
1997
|
-
|
1998
|
-
# Removes a recipient from a group channel.
|
1999
|
-
# @param recipient [Recipient] the recipient to remove from the group
|
2000
|
-
# @raise [ArgumentError] if tried to remove a non-recipient
|
2001
|
-
# @note For internal use only
|
2002
|
-
# @!visibility private
|
2003
|
-
def remove_recipient(recipient)
|
2004
|
-
raise 'Tried to remove recipient from a non-group channel' unless group?
|
2005
|
-
raise ArgumentError, 'Tried to remove a non-recipient from a group' unless recipient.is_a?(Recipient)
|
2006
|
-
@recipients.delete(recipient)
|
2007
|
-
end
|
2008
|
-
|
2009
|
-
# Updates the cached data with new data
|
2010
|
-
# @note For internal use only
|
2011
|
-
# @!visibility private
|
2012
|
-
def update_data(new_data = nil)
|
2013
|
-
new_data ||= JSON.parse(API::Channel.resolve(@bot.token, @id))
|
2014
|
-
@name = new_data[:name] || new_data['name'] || @name
|
2015
|
-
@topic = new_data[:topic] || new_data['topic'] || @topic
|
2016
|
-
@position = new_data[:position] || new_data['position'] || @position
|
2017
|
-
@bitrate = new_data[:bitrate] || new_data['bitrate'] || @bitrate
|
2018
|
-
@user_limit = new_data[:user_limit] || new_data['user_limit'] || @user_limit
|
2019
|
-
new_nsfw = new_data.key?(:nsfw) ? new_data[:nsfw] : new_data['nsfw']
|
2020
|
-
@nsfw = new_nsfw.nil? ? @nsfw : new_nsfw
|
2021
|
-
@parent_id = new_data[:parent_id] || new_data['parent_id'] || @parent_id
|
2022
|
-
process_permission_overwrites(new_data[:permission_overwrites] || new_data['permission_overwrites'])
|
2023
|
-
@rate_limit_per_user = new_data[:rate_limit_per_user] || new_data['rate_limit_per_user'] || @rate_limit_per_user
|
2024
|
-
end
|
2025
|
-
|
2026
|
-
private
|
2027
|
-
|
2028
|
-
# For bulk_delete checking
|
2029
|
-
TWO_WEEKS = 86_400 * 14
|
2030
|
-
|
2031
|
-
# Deletes a list of messages on this channel using bulk delete.
|
2032
|
-
def bulk_delete(ids, strict = false)
|
2033
|
-
min_snowflake = IDObject.synthesise(Time.now - TWO_WEEKS)
|
2034
|
-
|
2035
|
-
ids.reject! do |e|
|
2036
|
-
next unless e < min_snowflake
|
2037
|
-
|
2038
|
-
message = "Attempted to bulk_delete message #{e} which is too old (min = #{min_snowflake})"
|
2039
|
-
raise ArgumentError, message if strict
|
2040
|
-
Discordrb::LOGGER.warn(message)
|
2041
|
-
true
|
2042
|
-
end
|
2043
|
-
|
2044
|
-
API::Channel.bulk_delete_messages(@bot.token, @id, ids)
|
2045
|
-
ids.size
|
2046
|
-
end
|
2047
|
-
|
2048
|
-
def update_channel_data(new_data)
|
2049
|
-
new_nsfw = new_data[:nsfw].is_a?(TrueClass) || new_data[:nsfw].is_a?(FalseClass) ? new_nsfw : @nsfw
|
2050
|
-
# send permission_overwrite only when explicitly set
|
2051
|
-
overwrites = new_data[:permission_overwrites] ? new_data[:permission_overwrites].map { |_, v| v.to_hash } : nil
|
2052
|
-
response = JSON.parse(API::Channel.update(@bot.token, @id,
|
2053
|
-
new_data[:name] || @name,
|
2054
|
-
new_data[:topic] || @topic,
|
2055
|
-
new_data[:position] || @position,
|
2056
|
-
new_data[:bitrate] || @bitrate,
|
2057
|
-
new_data[:user_limit] || @user_limit,
|
2058
|
-
new_nsfw,
|
2059
|
-
overwrites,
|
2060
|
-
new_data[:parent_id] || @parent_id,
|
2061
|
-
new_data[:rate_limit_per_user] || @rate_limit_per_user))
|
2062
|
-
update_data(response)
|
2063
|
-
end
|
2064
|
-
|
2065
|
-
def process_permission_overwrites(overwrites)
|
2066
|
-
# Populate permission overwrites
|
2067
|
-
@permission_overwrites = {}
|
2068
|
-
return unless overwrites
|
2069
|
-
overwrites.each do |element|
|
2070
|
-
id = element['id'].to_i
|
2071
|
-
@permission_overwrites[id] = Overwrite.from_hash(element)
|
2072
|
-
end
|
2073
|
-
end
|
2074
|
-
end
|
2075
|
-
|
2076
|
-
# An Embed object that is contained in a message
|
2077
|
-
# A freshly generated embed object will not appear in a message object
|
2078
|
-
# unless grabbed from its ID in a channel.
|
2079
|
-
class Embed
|
2080
|
-
# @return [Message] the message this embed object is contained in.
|
2081
|
-
attr_reader :message
|
2082
|
-
|
2083
|
-
# @return [String] the URL this embed object is based on.
|
2084
|
-
attr_reader :url
|
2085
|
-
|
2086
|
-
# @return [String, nil] the title of the embed object. `nil` if there is not a title
|
2087
|
-
attr_reader :title
|
2088
|
-
|
2089
|
-
# @return [String, nil] the description of the embed object. `nil` if there is not a description
|
2090
|
-
attr_reader :description
|
2091
|
-
|
2092
|
-
# @return [Symbol] the type of the embed object. Possible types are:
|
2093
|
-
#
|
2094
|
-
# * `:link`
|
2095
|
-
# * `:video`
|
2096
|
-
# * `:image`
|
2097
|
-
attr_reader :type
|
2098
|
-
|
2099
|
-
# @return [Time, nil] the timestamp of the embed object. `nil` if there is not a timestamp
|
2100
|
-
attr_reader :timestamp
|
2101
|
-
|
2102
|
-
# @return [String, nil] the color of the embed object. `nil` if there is not a color
|
2103
|
-
attr_reader :color
|
2104
|
-
alias_method :colour, :color
|
2105
|
-
|
2106
|
-
# @return [EmbedFooter, nil] the footer of the embed object. `nil` if there is not a footer
|
2107
|
-
attr_reader :footer
|
2108
|
-
|
2109
|
-
# @return [EmbedProvider, nil] the provider of the embed object. `nil` if there is not a provider
|
2110
|
-
attr_reader :provider
|
2111
|
-
|
2112
|
-
# @return [EmbedImage, nil] the image of the embed object. `nil` if there is not an image
|
2113
|
-
attr_reader :image
|
2114
|
-
|
2115
|
-
# @return [EmbedThumbnail, nil] the thumbnail of the embed object. `nil` if there is not a thumbnail
|
2116
|
-
attr_reader :thumbnail
|
2117
|
-
|
2118
|
-
# @return [EmbedVideo, nil] the video of the embed object. `nil` if there is not a video
|
2119
|
-
attr_reader :video
|
2120
|
-
|
2121
|
-
# @return [EmbedAuthor, nil] the author of the embed object. `nil` if there is not an author
|
2122
|
-
attr_reader :author
|
2123
|
-
|
2124
|
-
# @return [Array<EmbedField>, nil] the fields of the embed object. `nil` if there are no fields
|
2125
|
-
attr_reader :fields
|
2126
|
-
|
2127
|
-
# @!visibility private
|
2128
|
-
def initialize(data, message)
|
2129
|
-
@message = message
|
2130
|
-
|
2131
|
-
@url = data['url']
|
2132
|
-
@title = data['title']
|
2133
|
-
@type = data['type'].to_sym
|
2134
|
-
@description = data['description']
|
2135
|
-
@timestamp = data['timestamp'].nil? ? nil : Time.parse(data['timestamp'])
|
2136
|
-
@color = data['color']
|
2137
|
-
@footer = data['footer'].nil? ? nil : EmbedFooter.new(data['footer'], self)
|
2138
|
-
@image = data['image'].nil? ? nil : EmbedImage.new(data['image'], self)
|
2139
|
-
@video = data['video'].nil? ? nil : EmbedVideo.new(data['video'], self)
|
2140
|
-
@provider = data['provider'].nil? ? nil : EmbedProvider.new(data['provider'], self)
|
2141
|
-
@thumbnail = data['thumbnail'].nil? ? nil : EmbedThumbnail.new(data['thumbnail'], self)
|
2142
|
-
@author = data['author'].nil? ? nil : EmbedAuthor.new(data['author'], self)
|
2143
|
-
@fields = data['fields'].nil? ? nil : data['fields'].map { |field| EmbedField.new(field, self) }
|
2144
|
-
end
|
2145
|
-
end
|
2146
|
-
|
2147
|
-
# An Embed footer for the embed object.
|
2148
|
-
class EmbedFooter
|
2149
|
-
# @return [Embed] the embed object this is based on.
|
2150
|
-
attr_reader :embed
|
2151
|
-
|
2152
|
-
# @return [String] the footer text.
|
2153
|
-
attr_reader :text
|
2154
|
-
|
2155
|
-
# @return [String] the URL of the footer icon.
|
2156
|
-
attr_reader :icon_url
|
2157
|
-
|
2158
|
-
# @return [String] the proxied URL of the footer icon.
|
2159
|
-
attr_reader :proxy_icon_url
|
2160
|
-
|
2161
|
-
# @!visibility private
|
2162
|
-
def initialize(data, embed)
|
2163
|
-
@embed = embed
|
2164
|
-
|
2165
|
-
@text = data['text']
|
2166
|
-
@icon_url = data['icon_url']
|
2167
|
-
@proxy_icon_url = data['proxy_icon_url']
|
2168
|
-
end
|
2169
|
-
end
|
2170
|
-
|
2171
|
-
# An Embed image for the embed object.
|
2172
|
-
class EmbedImage
|
2173
|
-
# @return [Embed] the embed object this is based on.
|
2174
|
-
attr_reader :embed
|
2175
|
-
|
2176
|
-
# @return [String] the source URL of the image.
|
2177
|
-
attr_reader :url
|
2178
|
-
|
2179
|
-
# @return [String] the proxy URL of the image.
|
2180
|
-
attr_reader :proxy_url
|
2181
|
-
|
2182
|
-
# @return [Integer] the width of the image, in pixels.
|
2183
|
-
attr_reader :width
|
2184
|
-
|
2185
|
-
# @return [Integer] the height of the image, in pixels.
|
2186
|
-
attr_reader :height
|
2187
|
-
|
2188
|
-
# @!visibility private
|
2189
|
-
def initialize(data, embed)
|
2190
|
-
@embed = embed
|
2191
|
-
|
2192
|
-
@url = data['url']
|
2193
|
-
@proxy_url = data['proxy_url']
|
2194
|
-
@width = data['width']
|
2195
|
-
@height = data['height']
|
2196
|
-
end
|
2197
|
-
end
|
2198
|
-
|
2199
|
-
# An Embed video for the embed object
|
2200
|
-
class EmbedVideo
|
2201
|
-
# @return [Embed] the embed object this is based on.
|
2202
|
-
attr_reader :embed
|
2203
|
-
|
2204
|
-
# @return [String] the source URL of the video.
|
2205
|
-
attr_reader :url
|
2206
|
-
|
2207
|
-
# @return [Integer] the width of the video, in pixels.
|
2208
|
-
attr_reader :width
|
2209
|
-
|
2210
|
-
# @return [Integer] the height of the video, in pixels.
|
2211
|
-
attr_reader :height
|
2212
|
-
|
2213
|
-
# @!visibility private
|
2214
|
-
def initialize(data, embed)
|
2215
|
-
@embed = embed
|
2216
|
-
|
2217
|
-
@url = data['url']
|
2218
|
-
@width = data['width']
|
2219
|
-
@height = data['height']
|
2220
|
-
end
|
2221
|
-
end
|
2222
|
-
|
2223
|
-
# An Embed thumbnail for the embed object
|
2224
|
-
class EmbedThumbnail
|
2225
|
-
# @return [Embed] the embed object this is based on.
|
2226
|
-
attr_reader :embed
|
2227
|
-
|
2228
|
-
# @return [String] the CDN URL this thumbnail can be downloaded at.
|
2229
|
-
attr_reader :url
|
2230
|
-
|
2231
|
-
# @return [String] the thumbnail's proxy URL - I'm not sure what exactly this does, but I think it has something to
|
2232
|
-
# do with CDNs.
|
2233
|
-
attr_reader :proxy_url
|
2234
|
-
|
2235
|
-
# @return [Integer] the width of this thumbnail file, in pixels.
|
2236
|
-
attr_reader :width
|
2237
|
-
|
2238
|
-
# @return [Integer] the height of this thumbnail file, in pixels.
|
2239
|
-
attr_reader :height
|
2240
|
-
|
2241
|
-
# @!visibility private
|
2242
|
-
def initialize(data, embed)
|
2243
|
-
@embed = embed
|
2244
|
-
|
2245
|
-
@url = data['url']
|
2246
|
-
@proxy_url = data['proxy_url']
|
2247
|
-
@width = data['width']
|
2248
|
-
@height = data['height']
|
2249
|
-
end
|
2250
|
-
end
|
2251
|
-
|
2252
|
-
# An Embed provider for the embed object
|
2253
|
-
class EmbedProvider
|
2254
|
-
# @return [Embed] the embed object this is based on.
|
2255
|
-
attr_reader :embed
|
2256
|
-
|
2257
|
-
# @return [String] the provider's name.
|
2258
|
-
attr_reader :name
|
2259
|
-
|
2260
|
-
# @return [String, nil] the URL of the provider, or `nil` if there is no URL.
|
2261
|
-
attr_reader :url
|
2262
|
-
|
2263
|
-
# @!visibility private
|
2264
|
-
def initialize(data, embed)
|
2265
|
-
@embed = embed
|
2266
|
-
|
2267
|
-
@name = data['name']
|
2268
|
-
@url = data['url']
|
2269
|
-
end
|
2270
|
-
end
|
2271
|
-
|
2272
|
-
# An Embed author for the embed object
|
2273
|
-
class EmbedAuthor
|
2274
|
-
# @return [Embed] the embed object this is based on.
|
2275
|
-
attr_reader :embed
|
2276
|
-
|
2277
|
-
# @return [String] the author's name.
|
2278
|
-
attr_reader :name
|
2279
|
-
|
2280
|
-
# @return [String, nil] the URL of the author's website, or `nil` if there is no URL.
|
2281
|
-
attr_reader :url
|
2282
|
-
|
2283
|
-
# @return [String, nil] the icon of the author, or `nil` if there is no icon.
|
2284
|
-
attr_reader :icon_url
|
2285
|
-
|
2286
|
-
# @return [String, nil] the Discord proxy URL, or `nil` if there is no `icon_url`.
|
2287
|
-
attr_reader :proxy_icon_url
|
2288
|
-
|
2289
|
-
# @!visibility private
|
2290
|
-
def initialize(data, embed)
|
2291
|
-
@embed = embed
|
2292
|
-
|
2293
|
-
@name = data['name']
|
2294
|
-
@url = data['url']
|
2295
|
-
@icon_url = data['icon_url']
|
2296
|
-
@proxy_icon_url = data['proxy_icon_url']
|
2297
|
-
end
|
2298
|
-
end
|
2299
|
-
|
2300
|
-
# An Embed field for the embed object
|
2301
|
-
class EmbedField
|
2302
|
-
# @return [Embed] the embed object this is based on.
|
2303
|
-
attr_reader :embed
|
2304
|
-
|
2305
|
-
# @return [String] the field's name.
|
2306
|
-
attr_reader :name
|
2307
|
-
|
2308
|
-
# @return [String] the field's value.
|
2309
|
-
attr_reader :value
|
2310
|
-
|
2311
|
-
# @return [true, false] whether this field is inline.
|
2312
|
-
attr_reader :inline
|
2313
|
-
|
2314
|
-
# @!visibility private
|
2315
|
-
def initialize(data, embed)
|
2316
|
-
@embed = embed
|
2317
|
-
|
2318
|
-
@name = data['name']
|
2319
|
-
@value = data['value']
|
2320
|
-
@inline = data['inline']
|
2321
|
-
end
|
2322
|
-
end
|
2323
|
-
|
2324
|
-
# An attachment to a message
|
2325
|
-
class Attachment
|
2326
|
-
include IDObject
|
2327
|
-
|
2328
|
-
# @return [Message] the message this attachment belongs to.
|
2329
|
-
attr_reader :message
|
2330
|
-
|
2331
|
-
# @return [String] the CDN URL this attachment can be downloaded at.
|
2332
|
-
attr_reader :url
|
2333
|
-
|
2334
|
-
# @return [String] the attachment's proxy URL - I'm not sure what exactly this does, but I think it has something to
|
2335
|
-
# do with CDNs.
|
2336
|
-
attr_reader :proxy_url
|
2337
|
-
|
2338
|
-
# @return [String] the attachment's filename.
|
2339
|
-
attr_reader :filename
|
2340
|
-
|
2341
|
-
# @return [Integer] the attachment's file size in bytes.
|
2342
|
-
attr_reader :size
|
2343
|
-
|
2344
|
-
# @return [Integer, nil] the width of an image file, in pixels, or `nil` if the file is not an image.
|
2345
|
-
attr_reader :width
|
2346
|
-
|
2347
|
-
# @return [Integer, nil] the height of an image file, in pixels, or `nil` if the file is not an image.
|
2348
|
-
attr_reader :height
|
2349
|
-
|
2350
|
-
# @!visibility private
|
2351
|
-
def initialize(data, message, bot)
|
2352
|
-
@bot = bot
|
2353
|
-
@message = message
|
2354
|
-
|
2355
|
-
@id = data['id'].to_i
|
2356
|
-
@url = data['url']
|
2357
|
-
@proxy_url = data['proxy_url']
|
2358
|
-
@filename = data['filename']
|
2359
|
-
|
2360
|
-
@size = data['size']
|
2361
|
-
|
2362
|
-
@width = data['width']
|
2363
|
-
@height = data['height']
|
2364
|
-
end
|
2365
|
-
|
2366
|
-
# @return [true, false] whether this file is an image file.
|
2367
|
-
def image?
|
2368
|
-
!(@width.nil? || @height.nil?)
|
2369
|
-
end
|
2370
|
-
end
|
2371
|
-
|
2372
|
-
# A message on Discord that was sent to a text channel
|
2373
|
-
class Message
|
2374
|
-
include IDObject
|
2375
|
-
|
2376
|
-
# @return [String] the content of this message.
|
2377
|
-
attr_reader :content
|
2378
|
-
alias_method :text, :content
|
2379
|
-
alias_method :to_s, :content
|
2380
|
-
|
2381
|
-
# @return [Member, User] the user that sent this message. (Will be a {Member} most of the time, it should only be a
|
2382
|
-
# {User} for old messages when the author has left the server since then)
|
2383
|
-
attr_reader :author
|
2384
|
-
alias_method :user, :author
|
2385
|
-
alias_method :writer, :author
|
2386
|
-
|
2387
|
-
# @return [Channel] the channel in which this message was sent.
|
2388
|
-
attr_reader :channel
|
2389
|
-
|
2390
|
-
# @return [Time] the timestamp at which this message was sent.
|
2391
|
-
attr_reader :timestamp
|
2392
|
-
|
2393
|
-
# @return [Time] the timestamp at which this message was edited. `nil` if the message was never edited.
|
2394
|
-
attr_reader :edited_timestamp
|
2395
|
-
alias_method :edit_timestamp, :edited_timestamp
|
2396
|
-
|
2397
|
-
# @return [Array<User>] the users that were mentioned in this message.
|
2398
|
-
attr_reader :mentions
|
2399
|
-
|
2400
|
-
# @return [Array<Role>] the roles that were mentioned in this message.
|
2401
|
-
attr_reader :role_mentions
|
2402
|
-
|
2403
|
-
# @return [Array<Attachment>] the files attached to this message.
|
2404
|
-
attr_reader :attachments
|
2405
|
-
|
2406
|
-
# @return [Array<Embed>] the embed objects contained in this message.
|
2407
|
-
attr_reader :embeds
|
2408
|
-
|
2409
|
-
# @return [Hash<String => Reaction>] the reaction objects attached to this message keyed by the name of the reaction
|
2410
|
-
attr_reader :reactions
|
2411
|
-
|
2412
|
-
# @return [true, false] whether the message used Text-To-Speech (TTS) or not.
|
2413
|
-
attr_reader :tts
|
2414
|
-
alias_method :tts?, :tts
|
2415
|
-
|
2416
|
-
# @return [String] used for validating a message was sent.
|
2417
|
-
attr_reader :nonce
|
2418
|
-
|
2419
|
-
# @return [true, false] whether the message was edited or not.
|
2420
|
-
attr_reader :edited
|
2421
|
-
alias_method :edited?, :edited
|
2422
|
-
|
2423
|
-
# @return [true, false] whether the message mentioned everyone or not.
|
2424
|
-
attr_reader :mention_everyone
|
2425
|
-
alias_method :mention_everyone?, :mention_everyone
|
2426
|
-
alias_method :mentions_everyone?, :mention_everyone
|
2427
|
-
|
2428
|
-
# @return [true, false] whether the message is pinned or not.
|
2429
|
-
attr_reader :pinned
|
2430
|
-
alias_method :pinned?, :pinned
|
2431
|
-
|
2432
|
-
# @return [Integer, nil] the webhook ID that sent this message, or `nil` if it wasn't sent through a webhook.
|
2433
|
-
attr_reader :webhook_id
|
2434
|
-
|
2435
|
-
# The discriminator that webhook user accounts have.
|
2436
|
-
ZERO_DISCRIM = '0000'.freeze
|
2437
|
-
|
2438
|
-
# @!visibility private
|
2439
|
-
def initialize(data, bot)
|
2440
|
-
@bot = bot
|
2441
|
-
@content = data['content']
|
2442
|
-
@channel = bot.channel(data['channel_id'].to_i)
|
2443
|
-
@pinned = data['pinned']
|
2444
|
-
@tts = data['tts']
|
2445
|
-
@nonce = data['nonce']
|
2446
|
-
@mention_everyone = data['mention_everyone']
|
2447
|
-
|
2448
|
-
@author = if data['author']
|
2449
|
-
if data['author']['discriminator'] == ZERO_DISCRIM
|
2450
|
-
# This is a webhook user! It would be pointless to try to resolve a member here, so we just create
|
2451
|
-
# a User and return that instead.
|
2452
|
-
Discordrb::LOGGER.debug("Webhook user: #{data['author']['id']}")
|
2453
|
-
User.new(data['author'], @bot)
|
2454
|
-
elsif @channel.private?
|
2455
|
-
# Turn the message user into a recipient - we can't use the channel recipient
|
2456
|
-
# directly because the bot may also send messages to the channel
|
2457
|
-
Recipient.new(bot.user(data['author']['id'].to_i), @channel, bot)
|
2458
|
-
else
|
2459
|
-
member = @channel.server.member(data['author']['id'].to_i)
|
2460
|
-
|
2461
|
-
unless member
|
2462
|
-
Discordrb::LOGGER.debug("Member with ID #{data['author']['id']} not cached (possibly left the server).")
|
2463
|
-
member = @bot.ensure_user(data['author'])
|
2464
|
-
end
|
2465
|
-
|
2466
|
-
member
|
2467
|
-
end
|
2468
|
-
end
|
2469
|
-
|
2470
|
-
@webhook_id = data['webhook_id'].to_i if data['webhook_id']
|
2471
|
-
|
2472
|
-
@timestamp = Time.parse(data['timestamp']) if data['timestamp']
|
2473
|
-
@edited_timestamp = data['edited_timestamp'].nil? ? nil : Time.parse(data['edited_timestamp'])
|
2474
|
-
@edited = !@edited_timestamp.nil?
|
2475
|
-
@id = data['id'].to_i
|
2476
|
-
|
2477
|
-
@emoji = []
|
2478
|
-
|
2479
|
-
@reactions = {}
|
2480
|
-
|
2481
|
-
if data['reactions']
|
2482
|
-
data['reactions'].each do |element|
|
2483
|
-
@reactions[element['emoji']['name']] = Reaction.new(element)
|
2484
|
-
end
|
2485
|
-
end
|
2486
|
-
|
2487
|
-
@mentions = []
|
2488
|
-
|
2489
|
-
if data['mentions']
|
2490
|
-
data['mentions'].each do |element|
|
2491
|
-
@mentions << bot.ensure_user(element)
|
2492
|
-
end
|
2493
|
-
end
|
2494
|
-
|
2495
|
-
@role_mentions = []
|
2496
|
-
|
2497
|
-
# Role mentions can only happen on public servers so make sure we only parse them there
|
2498
|
-
if @channel.text?
|
2499
|
-
if data['mention_roles']
|
2500
|
-
data['mention_roles'].each do |element|
|
2501
|
-
@role_mentions << @channel.server.role(element.to_i)
|
2502
|
-
end
|
2503
|
-
end
|
2504
|
-
end
|
2505
|
-
|
2506
|
-
@attachments = []
|
2507
|
-
@attachments = data['attachments'].map { |e| Attachment.new(e, self, @bot) } if data['attachments']
|
2508
|
-
|
2509
|
-
@embeds = []
|
2510
|
-
@embeds = data['embeds'].map { |e| Embed.new(e, self) } if data['embeds']
|
2511
|
-
end
|
2512
|
-
|
2513
|
-
# Replies to this message with the specified content.
|
2514
|
-
# @see Channel#send_message
|
2515
|
-
def reply(content)
|
2516
|
-
@channel.send_message(content)
|
2517
|
-
end
|
2518
|
-
|
2519
|
-
# Edits this message to have the specified content instead.
|
2520
|
-
# You can only edit your own messages.
|
2521
|
-
# @param new_content [String] the new content the message should have.
|
2522
|
-
# @param new_embed [Hash, Discordrb::Webhooks::Embed, nil] The new embed the message should have. If `nil` the message will be changed to have no embed.
|
2523
|
-
# @return [Message] the resulting message.
|
2524
|
-
def edit(new_content, new_embed = nil)
|
2525
|
-
response = API::Channel.edit_message(@bot.token, @channel.id, @id, new_content, [], new_embed ? new_embed.to_hash : nil)
|
2526
|
-
Message.new(JSON.parse(response), @bot)
|
2527
|
-
end
|
2528
|
-
|
2529
|
-
# Deletes this message.
|
2530
|
-
def delete
|
2531
|
-
API::Channel.delete_message(@bot.token, @channel.id, @id)
|
2532
|
-
nil
|
2533
|
-
end
|
2534
|
-
|
2535
|
-
# Pins this message
|
2536
|
-
def pin
|
2537
|
-
API::Channel.pin_message(@bot.token, @channel.id, @id)
|
2538
|
-
@pinned = true
|
2539
|
-
nil
|
2540
|
-
end
|
2541
|
-
|
2542
|
-
# Unpins this message
|
2543
|
-
def unpin
|
2544
|
-
API::Channel.unpin_message(@bot.token, @channel.id, @id)
|
2545
|
-
@pinned = false
|
2546
|
-
nil
|
2547
|
-
end
|
2548
|
-
|
2549
|
-
# Add an {Await} for a message with the same user and channel.
|
2550
|
-
# @see Bot#add_await
|
2551
|
-
# @deprecated Will be changed to blocking behavior in v4.0. Use {#await!} instead.
|
2552
|
-
def await(key, attributes = {}, &block)
|
2553
|
-
@bot.add_await(key, Discordrb::Events::MessageEvent, { from: @author.id, in: @channel.id }.merge(attributes), &block)
|
2554
|
-
end
|
2555
|
-
|
2556
|
-
# Add a blocking {Await} for a message with the same user and channel.
|
2557
|
-
# @see Bot#add_await!
|
2558
|
-
def await!(attributes = {})
|
2559
|
-
@bot.add_await!(Discordrb::Events::MessageEvent, { from: @author.id, in: @channel.id }.merge(attributes))
|
2560
|
-
end
|
2561
|
-
|
2562
|
-
# @return [true, false] whether this message was sent by the current {Bot}.
|
2563
|
-
def from_bot?
|
2564
|
-
@author && @author.current_bot?
|
2565
|
-
end
|
2566
|
-
|
2567
|
-
# @return [true, false] whether this message has been sent over a webhook.
|
2568
|
-
def webhook?
|
2569
|
-
!@webhook_id.nil?
|
2570
|
-
end
|
2571
|
-
|
2572
|
-
# @!visibility private
|
2573
|
-
# @return [Array<String>] the emoji mentions found in the message
|
2574
|
-
def scan_for_emoji
|
2575
|
-
emoji = @content.split
|
2576
|
-
emoji = emoji.grep(/<(?<animated>a)?:(?<name>\w+):(?<id>\d+)>?/)
|
2577
|
-
emoji
|
2578
|
-
end
|
2579
|
-
|
2580
|
-
# @return [Array<Emoji>] the emotes that were used/mentioned in this message.
|
2581
|
-
def emoji
|
2582
|
-
return if @content.nil?
|
2583
|
-
|
2584
|
-
emoji = scan_for_emoji
|
2585
|
-
emoji.each do |element|
|
2586
|
-
@emoji << @bot.parse_mention(element)
|
2587
|
-
end
|
2588
|
-
@emoji
|
2589
|
-
end
|
2590
|
-
|
2591
|
-
# Check if any emoji were used in this message.
|
2592
|
-
# @return [true, false] whether or not any emoji were used
|
2593
|
-
def emoji?
|
2594
|
-
emoji = scan_for_emoji
|
2595
|
-
return true unless emoji.empty?
|
2596
|
-
end
|
2597
|
-
|
2598
|
-
# Check if any reactions were used in this message.
|
2599
|
-
# @return [true, false] whether or not this message has reactions
|
2600
|
-
def reactions?
|
2601
|
-
@reactions.any?
|
2602
|
-
end
|
2603
|
-
|
2604
|
-
# Returns the reactions made by the current bot or user.
|
2605
|
-
# @return [Array<Reaction>] the reactions
|
2606
|
-
def my_reactions
|
2607
|
-
@reactions.values.select(&:me)
|
2608
|
-
end
|
2609
|
-
|
2610
|
-
# Reacts to a message.
|
2611
|
-
# @param reaction [String, #to_reaction] the unicode emoji or {Emoji}
|
2612
|
-
def create_reaction(reaction)
|
2613
|
-
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
2614
|
-
API::Channel.create_reaction(@bot.token, @channel.id, @id, reaction)
|
2615
|
-
nil
|
2616
|
-
end
|
2617
|
-
|
2618
|
-
alias_method :react, :create_reaction
|
2619
|
-
|
2620
|
-
# Returns the list of users who reacted with a certain reaction.
|
2621
|
-
# @param reaction [String, #to_reaction] the unicode emoji or {Emoji}
|
2622
|
-
# @example Get all the users that reacted with a thumbsup.
|
2623
|
-
# thumbs_up_reactions = message.reacted_with("\u{1F44D}")
|
2624
|
-
# @return [Array<User>] the users who used this reaction
|
2625
|
-
def reacted_with(reaction)
|
2626
|
-
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
2627
|
-
response = JSON.parse(API::Channel.get_reactions(@bot.token, @channel.id, @id, reaction))
|
2628
|
-
response.map { |d| User.new(d, @bot) }
|
2629
|
-
end
|
2630
|
-
|
2631
|
-
# Deletes a reaction made by a user on this message.
|
2632
|
-
# @param user [User, #resolve_id] the user who used this reaction
|
2633
|
-
# @param reaction [String, #to_reaction] the reaction to remove
|
2634
|
-
def delete_reaction(user, reaction)
|
2635
|
-
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
2636
|
-
API::Channel.delete_user_reaction(@bot.token, @channel.id, @id, reaction, user.resolve_id)
|
2637
|
-
end
|
2638
|
-
|
2639
|
-
# Deletes this client's reaction on this message.
|
2640
|
-
# @param reaction [String, #to_reaction] the reaction to remove
|
2641
|
-
def delete_own_reaction(reaction)
|
2642
|
-
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
2643
|
-
API::Channel.delete_own_reaction(@bot.token, @channel.id, @id, reaction)
|
2644
|
-
end
|
2645
|
-
|
2646
|
-
# Removes all reactions from this message.
|
2647
|
-
def delete_all_reactions
|
2648
|
-
API::Channel.delete_all_reactions(@bot.token, @channel.id, @id)
|
2649
|
-
end
|
2650
|
-
|
2651
|
-
# The inspect method is overwritten to give more useful output
|
2652
|
-
def inspect
|
2653
|
-
"<Message content=\"#{@content}\" id=#{@id} timestamp=#{@timestamp} author=#{@author} channel=#{@channel}>"
|
2654
|
-
end
|
2655
|
-
end
|
2656
|
-
|
2657
|
-
# A reaction to a message.
|
2658
|
-
class Reaction
|
2659
|
-
# @return [Integer] the amount of users who have reacted with this reaction
|
2660
|
-
attr_reader :count
|
2661
|
-
|
2662
|
-
# @return [true, false] whether the current bot or user used this reaction
|
2663
|
-
attr_reader :me
|
2664
|
-
alias_method :me?, :me
|
2665
|
-
|
2666
|
-
# @return [Integer] the ID of the emoji, if it was custom
|
2667
|
-
attr_reader :id
|
2668
|
-
|
2669
|
-
# @return [String] the name or unicode representation of the emoji
|
2670
|
-
attr_reader :name
|
2671
|
-
|
2672
|
-
def initialize(data)
|
2673
|
-
@count = data['count']
|
2674
|
-
@me = data['me']
|
2675
|
-
@id = data['emoji']['id'].nil? ? nil : data['emoji']['id'].to_i
|
2676
|
-
@name = data['emoji']['name']
|
2677
|
-
end
|
2678
|
-
|
2679
|
-
# Converts this Reaction into a string that can be sent back to Discord in other reaction endpoints.
|
2680
|
-
# If ID is present, it will be rendered into the form of `name:id`.
|
2681
|
-
# @return [String] the name of this reaction, including the ID if it is a custom emoji
|
2682
|
-
def to_s
|
2683
|
-
id.nil? ? name : "#{name}:#{id}"
|
2684
|
-
end
|
2685
|
-
end
|
2686
|
-
|
2687
|
-
# Server emoji
|
2688
|
-
class Emoji
|
2689
|
-
include IDObject
|
2690
|
-
|
2691
|
-
# @return [String] the emoji name
|
2692
|
-
attr_reader :name
|
2693
|
-
|
2694
|
-
# @return [Server] the server of this emoji
|
2695
|
-
attr_reader :server
|
2696
|
-
|
2697
|
-
# @return [Array<Role>] roles this emoji is active for
|
2698
|
-
attr_reader :roles
|
2699
|
-
|
2700
|
-
# @return [true, false] if the emoji is animated
|
2701
|
-
attr_reader :animated
|
2702
|
-
alias_method :animated?, :animated
|
2703
|
-
|
2704
|
-
def initialize(data, bot, server)
|
2705
|
-
@bot = bot
|
2706
|
-
@roles = nil
|
2707
|
-
|
2708
|
-
@name = data['name']
|
2709
|
-
@server = server
|
2710
|
-
@id = data['id'].nil? ? nil : data['id'].to_i
|
2711
|
-
@animated = data['animated']
|
2712
|
-
|
2713
|
-
process_roles(data['roles']) if server
|
2714
|
-
end
|
2715
|
-
|
2716
|
-
# @return [String] the layout to mention it (or have it used) in a message
|
2717
|
-
def mention
|
2718
|
-
"<#{'a' if animated}:#{name}:#{id}>"
|
2719
|
-
end
|
2720
|
-
|
2721
|
-
alias_method :use, :mention
|
2722
|
-
alias_method :to_s, :mention
|
2723
|
-
|
2724
|
-
# @return [String] the layout to use this emoji in a reaction
|
2725
|
-
def to_reaction
|
2726
|
-
"#{name}:#{id}"
|
2727
|
-
end
|
2728
|
-
|
2729
|
-
# @return [String] the icon URL of the emoji
|
2730
|
-
def icon_url
|
2731
|
-
API.emoji_icon_url(id)
|
2732
|
-
end
|
2733
|
-
|
2734
|
-
# The inspect method is overwritten to give more useful output
|
2735
|
-
def inspect
|
2736
|
-
"<Emoji name=#{name} id=#{id} animated=#{animated}>"
|
2737
|
-
end
|
2738
|
-
|
2739
|
-
# @!visibility private
|
2740
|
-
def process_roles(roles)
|
2741
|
-
@roles = []
|
2742
|
-
return unless roles
|
2743
|
-
roles.each do |role_id|
|
2744
|
-
role = server.role(role_id)
|
2745
|
-
@roles << role
|
2746
|
-
end
|
2747
|
-
end
|
2748
|
-
end
|
2749
|
-
|
2750
|
-
# Basic attributes a server should have
|
2751
|
-
module ServerAttributes
|
2752
|
-
# @return [String] this server's name.
|
2753
|
-
attr_reader :name
|
2754
|
-
|
2755
|
-
# @return [String] the hexadecimal ID used to identify this server's icon.
|
2756
|
-
attr_reader :icon_id
|
2757
|
-
|
2758
|
-
# Utility function to get the URL for the icon image
|
2759
|
-
# @return [String] the URL to the icon image
|
2760
|
-
def icon_url
|
2761
|
-
return nil unless @icon_id
|
2762
|
-
API.icon_url(@id, @icon_id)
|
2763
|
-
end
|
2764
|
-
end
|
2765
|
-
|
2766
|
-
# Integration Account
|
2767
|
-
class IntegrationAccount
|
2768
|
-
# @return [String] this account's name.
|
2769
|
-
attr_reader :name
|
2770
|
-
|
2771
|
-
# @return [Integer] this account's ID.
|
2772
|
-
attr_reader :id
|
2773
|
-
|
2774
|
-
def initialize(data)
|
2775
|
-
@name = data['name']
|
2776
|
-
@id = data['id'].to_i
|
2777
|
-
end
|
2778
|
-
end
|
2779
|
-
|
2780
|
-
# Server integration
|
2781
|
-
class Integration
|
2782
|
-
include IDObject
|
2783
|
-
|
2784
|
-
# @return [String] the integration name
|
2785
|
-
attr_reader :name
|
2786
|
-
|
2787
|
-
# @return [Server] the server the integration is linked to
|
2788
|
-
attr_reader :server
|
2789
|
-
|
2790
|
-
# @return [User] the user the integration is linked to
|
2791
|
-
attr_reader :user
|
2792
|
-
|
2793
|
-
# @return [Role, nil] the role that this integration uses for "subscribers"
|
2794
|
-
attr_reader :role
|
2795
|
-
|
2796
|
-
# @return [true, false] whether emoticons are enabled
|
2797
|
-
attr_reader :emoticon
|
2798
|
-
alias_method :emoticon?, :emoticon
|
2799
|
-
|
2800
|
-
# @return [String] the integration type (YouTube, Twitch, etc.)
|
2801
|
-
attr_reader :type
|
2802
|
-
|
2803
|
-
# @return [true, false] whether the integration is enabled
|
2804
|
-
attr_reader :enabled
|
2805
|
-
|
2806
|
-
# @return [true, false] whether the integration is syncing
|
2807
|
-
attr_reader :syncing
|
2808
|
-
|
2809
|
-
# @return [IntegrationAccount] the integration account information
|
2810
|
-
attr_reader :account
|
2811
|
-
|
2812
|
-
# @return [Time] the time the integration was synced at
|
2813
|
-
attr_reader :synced_at
|
2814
|
-
|
2815
|
-
# @return [Symbol] the behaviour of expiring subscribers (:remove = Remove User from role; :kick = Kick User from server)
|
2816
|
-
attr_reader :expire_behaviour
|
2817
|
-
alias_method :expire_behavior, :expire_behaviour
|
2818
|
-
|
2819
|
-
# @return [Integer] the grace period before subscribers expire (in days)
|
2820
|
-
attr_reader :expire_grace_period
|
2821
|
-
|
2822
|
-
def initialize(data, bot, server)
|
2823
|
-
@bot = bot
|
2824
|
-
|
2825
|
-
@name = data['name']
|
2826
|
-
@server = server
|
2827
|
-
@id = data['id'].to_i
|
2828
|
-
@enabled = data['enabled']
|
2829
|
-
@syncing = data['syncing']
|
2830
|
-
@type = data['type']
|
2831
|
-
@account = IntegrationAccount.new(data['account'])
|
2832
|
-
@synced_at = Time.parse(data['synced_at'])
|
2833
|
-
@expire_behaviour = %i[remove kick][data['expire_behavior']]
|
2834
|
-
@expire_grace_period = data['expire_grace_period']
|
2835
|
-
@user = @bot.ensure_user(data['user'])
|
2836
|
-
@role = server.role(data['role_id']) || nil
|
2837
|
-
@emoticon = data['enable_emoticons']
|
2838
|
-
end
|
2839
|
-
|
2840
|
-
# The inspect method is overwritten to give more useful output
|
2841
|
-
def inspect
|
2842
|
-
"<Integration name=#{@name} id=#{@id} type=#{@type} enabled=#{@enabled}>"
|
2843
|
-
end
|
2844
|
-
end
|
2845
|
-
|
2846
|
-
# A server on Discord
|
2847
|
-
class Server
|
2848
|
-
include IDObject
|
2849
|
-
include ServerAttributes
|
2850
|
-
|
2851
|
-
# @return [String] the ID of the region the server is on (e.g. `amsterdam`).
|
2852
|
-
attr_reader :region_id
|
2853
|
-
|
2854
|
-
# @return [Member] The server owner.
|
2855
|
-
attr_reader :owner
|
2856
|
-
|
2857
|
-
# @return [Array<Channel>] an array of all the channels (text and voice) on this server.
|
2858
|
-
attr_reader :channels
|
2859
|
-
|
2860
|
-
# @return [Array<Role>] an array of all the roles created on this server.
|
2861
|
-
attr_reader :roles
|
2862
|
-
|
2863
|
-
# @return [Hash<Integer => Emoji>] a hash of all the emoji available on this server.
|
2864
|
-
attr_reader :emoji
|
2865
|
-
alias_method :emojis, :emoji
|
2866
|
-
|
2867
|
-
# @return [true, false] whether or not this server is large (members > 100). If it is,
|
2868
|
-
# it means the members list may be inaccurate for a couple seconds after starting up the bot.
|
2869
|
-
attr_reader :large
|
2870
|
-
alias_method :large?, :large
|
2871
|
-
|
2872
|
-
# @return [Array<Symbol>] the features of the server (eg. "INVITE_SPLASH")
|
2873
|
-
attr_reader :features
|
2874
|
-
|
2875
|
-
# @return [Integer] the absolute number of members on this server, offline or not.
|
2876
|
-
attr_reader :member_count
|
2877
|
-
|
2878
|
-
# @return [Integer] the amount of time after which a voice user gets moved into the AFK channel, in seconds.
|
2879
|
-
attr_reader :afk_timeout
|
2880
|
-
|
2881
|
-
# @return [Hash<Integer => VoiceState>] the hash (user ID => voice state) of voice states of members on this server
|
2882
|
-
attr_reader :voice_states
|
2883
|
-
|
2884
|
-
# @!visibility private
|
2885
|
-
def initialize(data, bot, exists = true)
|
2886
|
-
@bot = bot
|
2887
|
-
@owner_id = data['owner_id'].to_i
|
2888
|
-
@id = data['id'].to_i
|
2889
|
-
|
2890
|
-
process_channels(data['channels'])
|
2891
|
-
update_data(data)
|
2892
|
-
|
2893
|
-
@large = data['large']
|
2894
|
-
@member_count = data['member_count']
|
2895
|
-
@splash_id = nil
|
2896
|
-
@features = data['features'].map { |element| element.downcase.to_sym }
|
2897
|
-
@members = {}
|
2898
|
-
@voice_states = {}
|
2899
|
-
@emoji = {}
|
2900
|
-
|
2901
|
-
process_roles(data['roles'])
|
2902
|
-
process_emoji(data['emojis'])
|
2903
|
-
process_members(data['members'])
|
2904
|
-
process_presences(data['presences'])
|
2905
|
-
process_voice_states(data['voice_states'])
|
2906
|
-
|
2907
|
-
# Whether this server's members have been chunked (resolved using op 8 and GUILD_MEMBERS_CHUNK) yet
|
2908
|
-
@chunked = false
|
2909
|
-
@processed_chunk_members = 0
|
2910
|
-
|
2911
|
-
# Only get the owner of the server actually exists (i.e. not for ServerDeleteEvent)
|
2912
|
-
@owner = member(@owner_id) if exists
|
2913
|
-
end
|
2914
|
-
|
2915
|
-
# The default channel is the text channel on this server with the highest position
|
2916
|
-
# that the bot has Read Messages permission on.
|
2917
|
-
# @param send_messages [true, false] whether to additionally consider if the bot has Send Messages permission
|
2918
|
-
# @return [Channel, nil] The default channel on this server, or `nil` if there are no channels that the bot can read.
|
2919
|
-
def default_channel(send_messages = false)
|
2920
|
-
bot_member = member(@bot.profile)
|
2921
|
-
text_channels.sort_by { |e| [e.position, e.id] }.find do |e|
|
2922
|
-
if send_messages
|
2923
|
-
bot_member.can_read_messages?(e) && bot_member.can_send_messages?(e)
|
2924
|
-
else
|
2925
|
-
bot_member.can_read_messages?(e)
|
2926
|
-
end
|
2927
|
-
end
|
2928
|
-
end
|
2929
|
-
|
2930
|
-
alias_method :general_channel, :default_channel
|
2931
|
-
|
2932
|
-
# @return [Role] The @everyone role on this server
|
2933
|
-
def everyone_role
|
2934
|
-
role(@id)
|
2935
|
-
end
|
2936
|
-
|
2937
|
-
# Gets a role on this server based on its ID.
|
2938
|
-
# @param id [Integer, String, #resolve_id] The role ID to look for.
|
2939
|
-
def role(id)
|
2940
|
-
id = id.resolve_id
|
2941
|
-
@roles.find { |e| e.id == id }
|
2942
|
-
end
|
2943
|
-
|
2944
|
-
# Gets a member on this server based on user ID
|
2945
|
-
# @param id [Integer] The user ID to look for
|
2946
|
-
# @param request [true, false] Whether the member should be requested from Discord if it's not cached
|
2947
|
-
def member(id, request = true)
|
2948
|
-
id = id.resolve_id
|
2949
|
-
return @members[id] if member_cached?(id)
|
2950
|
-
return nil unless request
|
2951
|
-
|
2952
|
-
member = @bot.member(self, id)
|
2953
|
-
@members[id] = member unless member.nil?
|
2954
|
-
rescue
|
2955
|
-
nil
|
2956
|
-
end
|
2957
|
-
|
2958
|
-
# @return [Array<Member>] an array of all the members on this server.
|
2959
|
-
def members
|
2960
|
-
return @members.values if @chunked
|
2961
|
-
|
2962
|
-
@bot.debug("Members for server #{@id} not chunked yet - initiating")
|
2963
|
-
@bot.request_chunks(@id)
|
2964
|
-
sleep 0.05 until @chunked
|
2965
|
-
@members.values
|
2966
|
-
end
|
2967
|
-
|
2968
|
-
alias_method :users, :members
|
2969
|
-
|
2970
|
-
# @return [Array<Integration>] an array of all the integrations connected to this server.
|
2971
|
-
def integrations
|
2972
|
-
integration = JSON.parse(API::Server.integrations(@bot.token, @id))
|
2973
|
-
integration.map { |element| Integration.new(element, @bot, self) }
|
2974
|
-
end
|
2975
|
-
|
2976
|
-
# @param action [Symbol] The action to only include.
|
2977
|
-
# @param user [User, #resolve_id] The user to filter entries to.
|
2978
|
-
# @param limit [Integer] The amount of entries to limit it to.
|
2979
|
-
# @param before [Entry, #resolve_id] The entry to use to not include all entries after it.
|
2980
|
-
# @return [AuditLogs] The server's audit logs.
|
2981
|
-
def audit_logs(action: nil, user: nil, limit: 50, before: nil)
|
2982
|
-
raise 'Invalid audit log action!' if action && AuditLogs::Actions.key(action).nil?
|
2983
|
-
action = AuditLogs::Actions.key(action)
|
2984
|
-
user = user.resolve_id if user
|
2985
|
-
before = before.resolve_id if before
|
2986
|
-
AuditLogs.new(self, @bot, JSON.parse(API::Server.audit_logs(@bot.token, @id, limit, user, action, before)))
|
2987
|
-
end
|
2988
|
-
|
2989
|
-
# Cache @embed
|
2990
|
-
# @note For internal use only
|
2991
|
-
# @!visibility private
|
2992
|
-
def cache_embed_data
|
2993
|
-
data = JSON.parse(API::Server.embed(@bot.token, @id))
|
2994
|
-
@embed_enabled = data['enabled']
|
2995
|
-
@embed_channel_id = data['channel_id']
|
2996
|
-
end
|
2997
|
-
|
2998
|
-
# @return [true, false] whether or not the server has widget enabled
|
2999
|
-
def embed_enabled?
|
3000
|
-
cache_embed_data if @embed_enabled.nil?
|
3001
|
-
@embed_enabled
|
3002
|
-
end
|
3003
|
-
alias_method :widget_enabled, :embed_enabled?
|
3004
|
-
alias_method :widget?, :embed_enabled?
|
3005
|
-
alias_method :embed?, :embed_enabled?
|
3006
|
-
|
3007
|
-
# @return [Channel, nil] the channel the server embed will make an invite for.
|
3008
|
-
def embed_channel
|
3009
|
-
cache_embed_data if @embed_enabled.nil?
|
3010
|
-
@bot.channel(@embed_channel_id) if @embed_channel_id
|
3011
|
-
end
|
3012
|
-
alias_method :widget_channel, :embed_channel
|
3013
|
-
|
3014
|
-
# Sets whether this server's embed (widget) is enabled
|
3015
|
-
# @param value [true, false]
|
3016
|
-
def embed_enabled=(value)
|
3017
|
-
modify_embed(value, embed_channel)
|
3018
|
-
end
|
3019
|
-
|
3020
|
-
alias_method :widget_enabled=, :embed_enabled=
|
3021
|
-
|
3022
|
-
# Sets whether this server's embed (widget) is enabled
|
3023
|
-
# @param value [true, false]
|
3024
|
-
# @param reason [String, nil] the reason to be shown in the audit log for this action
|
3025
|
-
def set_embed_enabled(value, reason = nil)
|
3026
|
-
modify_embed(value, embed_channel, reason)
|
3027
|
-
end
|
3028
|
-
|
3029
|
-
alias_method :set_widget_enabled, :set_embed_enabled
|
3030
|
-
|
3031
|
-
# Changes the channel on the server's embed (widget)
|
3032
|
-
# @param channel [Channel, String, Integer, #resolve_id] the channel to be referenced by the embed
|
3033
|
-
def embed_channel=(channel)
|
3034
|
-
modify_embed(embed?, channel)
|
3035
|
-
end
|
3036
|
-
|
3037
|
-
alias_method :widget_channel=, :embed_channel=
|
3038
|
-
|
3039
|
-
# Changes the channel on the server's embed (widget)
|
3040
|
-
# @param channel [Channel, String, Integer, #resolve_id] the channel to be referenced by the embed
|
3041
|
-
# @param reason [String, nil] the reason to be shown in the audit log for this action
|
3042
|
-
def set_embed_channel(channel, reason = nil)
|
3043
|
-
modify_embed(embed?, channel, reason)
|
3044
|
-
end
|
3045
|
-
|
3046
|
-
alias_method :set_widget_channel, :set_embed_channel
|
3047
|
-
|
3048
|
-
# Changes the channel on the server's embed (widget), and sets whether it is enabled.
|
3049
|
-
# @param enabled [true, false] whether the embed (widget) is enabled
|
3050
|
-
# @param channel [Channel, String, Integer, #resolve_id] the channel to be referenced by the embed
|
3051
|
-
# @param reason [String, nil] the reason to be shown in the audit log for this action
|
3052
|
-
def modify_embed(enabled, channel, reason = nil)
|
3053
|
-
cache_embed_data if @embed_enabled.nil?
|
3054
|
-
channel_id = channel ? channel.resolve_id : @embed_channel_id
|
3055
|
-
response = JSON.parse(API::Server.modify_embed(@bot.token, @id, enabled, channel_id, reason))
|
3056
|
-
@embed_enabled = response['enabled']
|
3057
|
-
@embed_channel_id = response['channel_id']
|
3058
|
-
end
|
3059
|
-
|
3060
|
-
alias_method :modify_widget, :modify_embed
|
3061
|
-
|
3062
|
-
# @param include_idle [true, false] Whether to count idle members as online.
|
3063
|
-
# @param include_bots [true, false] Whether to include bot accounts in the count.
|
3064
|
-
# @return [Array<Member>] an array of online members on this server.
|
3065
|
-
def online_members(include_idle: false, include_bots: true)
|
3066
|
-
@members.values.select do |e|
|
3067
|
-
((include_idle ? e.idle? : false) || e.online?) && (include_bots ? true : !e.bot_account?)
|
3068
|
-
end
|
3069
|
-
end
|
3070
|
-
|
3071
|
-
alias_method :online_users, :online_members
|
3072
|
-
|
3073
|
-
# Adds a member to this guild that has granted this bot's application an OAuth2 access token
|
3074
|
-
# with the `guilds.join` scope.
|
3075
|
-
# For more information about Discord's OAuth2 implementation, see: https://discordapp.com/developers/docs/topics/oauth2
|
3076
|
-
# @note Your bot must be present in this server, and have permission to create instant invites for this to work.
|
3077
|
-
# @param user [Integer, User, #resolve_id] the user, or ID of the user to add to this server
|
3078
|
-
# @param access_token [String] the OAuth2 Bearer token that has been granted the `guilds.join` scope
|
3079
|
-
# @param nick [String] the nickname to give this member upon joining
|
3080
|
-
# @param roles [Role, Array<Integer, Role, #resolve_id>] the role (or roles) to give this member upon joining
|
3081
|
-
# @param deaf [true, false] whether this member will be server deafened upon joining
|
3082
|
-
# @param mute [true, false] whether this member will be server muted upon joining
|
3083
|
-
# @return [Member] the created member
|
3084
|
-
def add_member_using_token(user, access_token, nick: nil, roles: [], deaf: false, mute: false)
|
3085
|
-
user_id = user.resolve_id
|
3086
|
-
roles = roles.is_a?(Array) ? roles.map(&:resolve_id) : [roles.resolve_id]
|
3087
|
-
response = JSON.parse(API::Server.add_member(@bot.token, @id, user_id, access_token, nick, roles, deaf, mute))
|
3088
|
-
add_member Member.new(response, self, @bot)
|
3089
|
-
end
|
3090
|
-
|
3091
|
-
# Returns the amount of members that are candidates for pruning
|
3092
|
-
# @param days [Integer] the number of days to consider for inactivity
|
3093
|
-
# @return [Integer] number of members to be removed
|
3094
|
-
# @raise [ArgumentError] if days is not between 1 and 30 (inclusive)
|
3095
|
-
def prune_count(days)
|
3096
|
-
raise ArgumentError, 'Days must be between 1 and 30' unless days.between?(1, 30)
|
3097
|
-
|
3098
|
-
response = JSON.parse API::Server.prune_count(@bot.token, @id, days)
|
3099
|
-
response['pruned']
|
3100
|
-
end
|
3101
|
-
|
3102
|
-
# Prunes (kicks) an amount of members for inactivity
|
3103
|
-
# @param days [Integer] the number of days to consider for inactivity (between 1 and 30)
|
3104
|
-
# @param reason [String] The reason the for the prune.
|
3105
|
-
# @return [Integer] the number of members removed at the end of the operation
|
3106
|
-
# @raise [ArgumentError] if days is not between 1 and 30 (inclusive)
|
3107
|
-
def begin_prune(days, reason = nil)
|
3108
|
-
raise ArgumentError, 'Days must be between 1 and 30' unless days.between?(1, 30)
|
3109
|
-
|
3110
|
-
response = JSON.parse API::Server.begin_prune(@bot.token, @id, days, reason)
|
3111
|
-
response['pruned']
|
3112
|
-
end
|
3113
|
-
|
3114
|
-
alias_method :prune, :begin_prune
|
3115
|
-
|
3116
|
-
# @return [Array<Channel>] an array of text channels on this server
|
3117
|
-
def text_channels
|
3118
|
-
@channels.select(&:text?)
|
3119
|
-
end
|
3120
|
-
|
3121
|
-
# @return [Array<Channel>] an array of voice channels on this server
|
3122
|
-
def voice_channels
|
3123
|
-
@channels.select(&:voice?)
|
3124
|
-
end
|
3125
|
-
|
3126
|
-
# @return [Array<Channel>] an array of category channels on this server
|
3127
|
-
def categories
|
3128
|
-
@channels.select(&:category?)
|
3129
|
-
end
|
3130
|
-
|
3131
|
-
# @return [Array<Channel>] an array of channels on this server that are not in a category
|
3132
|
-
def orphan_channels
|
3133
|
-
@channels.reject { |c| c.parent || c.category? }
|
3134
|
-
end
|
3135
|
-
|
3136
|
-
# @return [String, nil] the widget URL to the server that displays the amount of online members in a
|
3137
|
-
# stylish way. `nil` if the widget is not enabled.
|
3138
|
-
def widget_url
|
3139
|
-
update_data if @embed_enabled.nil?
|
3140
|
-
return unless @embed_enabled
|
3141
|
-
API.widget_url(@id)
|
3142
|
-
end
|
3143
|
-
|
3144
|
-
# @param style [Symbol] The style the picture should have. Possible styles are:
|
3145
|
-
# * `:banner1` creates a rectangular image with the server name, member count and icon, a "Powered by Discord" message on the bottom and an arrow on the right.
|
3146
|
-
# * `:banner2` creates a less tall rectangular image that has the same information as `banner1`, but the Discord logo on the right - together with the arrow and separated by a diagonal separator.
|
3147
|
-
# * `:banner3` creates an image similar in size to `banner1`, but it has the arrow in the bottom part, next to the Discord logo and with a "Chat now" text.
|
3148
|
-
# * `:banner4` creates a tall, almost square, image that prominently features the Discord logo at the top and has a "Join my server" in a pill-style button on the bottom. The information about the server is in the same format as the other three `banner` styles.
|
3149
|
-
# * `:shield` creates a very small, long rectangle, of the style you'd find at the top of GitHub `README.md` files. It features a small version of the Discord logo at the left and the member count at the right.
|
3150
|
-
# @return [String, nil] the widget banner URL to the server that displays the amount of online members,
|
3151
|
-
# server icon and server name in a stylish way. `nil` if the widget is not enabled.
|
3152
|
-
def widget_banner_url(style)
|
3153
|
-
update_data if @embed_enabled.nil?
|
3154
|
-
return unless @embed_enabled
|
3155
|
-
API.widget_url(@id, style)
|
3156
|
-
end
|
3157
|
-
|
3158
|
-
# @return [String] the hexadecimal ID used to identify this server's splash image for their VIP invite page.
|
3159
|
-
def splash_id
|
3160
|
-
@splash_id ||= JSON.parse(API::Server.resolve(@bot.token, @id))['splash']
|
3161
|
-
end
|
3162
|
-
|
3163
|
-
# @return [String, nil] the splash image URL for the server's VIP invite page.
|
3164
|
-
# `nil` if there is no splash image.
|
3165
|
-
def splash_url
|
3166
|
-
splash_id if @splash_id.nil?
|
3167
|
-
return nil unless @splash_id
|
3168
|
-
API.splash_url(@id, @splash_id)
|
3169
|
-
end
|
3170
|
-
|
3171
|
-
# Adds a role to the role cache
|
3172
|
-
# @note For internal use only
|
3173
|
-
# @!visibility private
|
3174
|
-
def add_role(role)
|
3175
|
-
@roles << role
|
3176
|
-
end
|
3177
|
-
|
3178
|
-
# Removes a role from the role cache
|
3179
|
-
# @note For internal use only
|
3180
|
-
# @!visibility private
|
3181
|
-
def delete_role(role_id)
|
3182
|
-
@roles.reject! { |r| r.id == role_id }
|
3183
|
-
@members.each do |_, member|
|
3184
|
-
new_roles = member.roles.reject { |r| r.id == role_id }
|
3185
|
-
member.update_roles(new_roles)
|
3186
|
-
end
|
3187
|
-
@channels.each do |channel|
|
3188
|
-
overwrites = channel.permission_overwrites.reject { |id, _| id == role_id }
|
3189
|
-
channel.update_overwrites(overwrites)
|
3190
|
-
end
|
3191
|
-
end
|
3192
|
-
|
3193
|
-
# Updates the positions of all roles on the server
|
3194
|
-
# @note For internal use only
|
3195
|
-
# @!visibility private
|
3196
|
-
def update_role_positions(role_positions)
|
3197
|
-
response = JSON.parse(API::Server.update_role_positions(@bot.token, @id, role_positions))
|
3198
|
-
response.each do |data|
|
3199
|
-
updated_role = Role.new(data, @bot, self)
|
3200
|
-
role(updated_role.id).update_from(updated_role)
|
3201
|
-
end
|
3202
|
-
end
|
3203
|
-
|
3204
|
-
# Adds a member to the member cache.
|
3205
|
-
# @note For internal use only
|
3206
|
-
# @!visibility private
|
3207
|
-
def add_member(member)
|
3208
|
-
@member_count += 1
|
3209
|
-
@members[member.id] = member
|
3210
|
-
end
|
3211
|
-
|
3212
|
-
# Removes a member from the member cache.
|
3213
|
-
# @note For internal use only
|
3214
|
-
# @!visibility private
|
3215
|
-
def delete_member(user_id)
|
3216
|
-
@members.delete(user_id)
|
3217
|
-
@member_count -= 1
|
3218
|
-
end
|
3219
|
-
|
3220
|
-
# Checks whether a member is cached
|
3221
|
-
# @note For internal use only
|
3222
|
-
# @!visibility private
|
3223
|
-
def member_cached?(user_id)
|
3224
|
-
@members.include?(user_id)
|
3225
|
-
end
|
3226
|
-
|
3227
|
-
# Adds a member to the cache
|
3228
|
-
# @note For internal use only
|
3229
|
-
# @!visibility private
|
3230
|
-
def cache_member(member)
|
3231
|
-
@members[member.id] = member
|
3232
|
-
end
|
3233
|
-
|
3234
|
-
# Updates a member's voice state
|
3235
|
-
# @note For internal use only
|
3236
|
-
# @!visibility private
|
3237
|
-
def update_voice_state(data)
|
3238
|
-
user_id = data['user_id'].to_i
|
3239
|
-
|
3240
|
-
if data['channel_id']
|
3241
|
-
unless @voice_states[user_id]
|
3242
|
-
# Create a new voice state for the user
|
3243
|
-
@voice_states[user_id] = VoiceState.new(user_id)
|
3244
|
-
end
|
3245
|
-
|
3246
|
-
# Update the existing voice state (or the one we just created)
|
3247
|
-
channel = @channels_by_id[data['channel_id'].to_i]
|
3248
|
-
@voice_states[user_id].update(
|
3249
|
-
channel,
|
3250
|
-
data['mute'],
|
3251
|
-
data['deaf'],
|
3252
|
-
data['self_mute'],
|
3253
|
-
data['self_deaf']
|
3254
|
-
)
|
3255
|
-
else
|
3256
|
-
# The user is not in a voice channel anymore, so delete its voice state
|
3257
|
-
@voice_states.delete(user_id)
|
3258
|
-
end
|
3259
|
-
end
|
3260
|
-
|
3261
|
-
# Creates a channel on this server with the given name.
|
3262
|
-
# @note If parent is provided, permission overwrites have the follow behavior:
|
3263
|
-
#
|
3264
|
-
# 1. If overwrites is null, the new channel inherits the parent's permissions.
|
3265
|
-
# 2. If overwrites is [], the new channel inherits the parent's permissions.
|
3266
|
-
# 3. If you supply one or more overwrites, the channel will be created with those permissions and ignore the parents.
|
3267
|
-
#
|
3268
|
-
# @param name [String] Name of the channel to create
|
3269
|
-
# @param type [Integer, Symbol] Type of channel to create (0: text, 2: voice, 4: category)
|
3270
|
-
# @param topic [String] the topic of this channel, if it will be a text channel
|
3271
|
-
# @param bitrate [Integer] the bitrate of this channel, if it will be a voice channel
|
3272
|
-
# @param user_limit [Integer] the user limit of this channel, if it will be a voice channel
|
3273
|
-
# @param permission_overwrites [Array<Hash>, Array<Overwrite>] permission overwrites for this channel
|
3274
|
-
# @param parent [Channel, #resolve_id] parent category for this channel to be created in.
|
3275
|
-
# @param nsfw [true, false] whether this channel should be created as nsfw
|
3276
|
-
# @param rate_limit_per_user [Integer] how many seconds users need to wait in between messages.
|
3277
|
-
# @param reason [String] The reason the for the creation of this channel.
|
3278
|
-
# @return [Channel] the created channel.
|
3279
|
-
# @raise [ArgumentError] if type is not 0 (text), 2 (voice), or 4 (category)
|
3280
|
-
def create_channel(name, type = 0, topic: nil, bitrate: nil, user_limit: nil, permission_overwrites: nil, parent: nil, nsfw: false, rate_limit_per_user: nil, reason: nil)
|
3281
|
-
type = Channel::TYPES[type] if type.is_a?(Symbol)
|
3282
|
-
raise ArgumentError, 'Channel type must be either 0 (text), 2 (voice), or 4 (category)!' unless [0, 2, 4].include?(type)
|
3283
|
-
permission_overwrites.map! { |e| e.is_a?(Overwrite) ? e.to_hash : e } if permission_overwrites.is_a?(Array)
|
3284
|
-
parent_id = parent.respond_to?(:resolve_id) ? parent.resolve_id : nil
|
3285
|
-
response = API::Server.create_channel(@bot.token, @id, name, type, topic, bitrate, user_limit, permission_overwrites, parent_id, nsfw, rate_limit_per_user, reason)
|
3286
|
-
Channel.new(JSON.parse(response), @bot)
|
3287
|
-
end
|
3288
|
-
|
3289
|
-
# Creates a role on this server which can then be modified. It will be initialized
|
3290
|
-
# with the regular role defaults the client uses unless specified, i.e. name is "new role",
|
3291
|
-
# permissions are the default, colour is the default etc.
|
3292
|
-
# @param name [String] Name of the role to create
|
3293
|
-
# @param colour [Integer, ColourRGB, #combined] The roles colour
|
3294
|
-
# @param hoist [true, false]
|
3295
|
-
# @param mentionable [true, false]
|
3296
|
-
# @param permissions [Integer, Array<Symbol>, Permissions, #bits] The permissions to write to the new role.
|
3297
|
-
# @param reason [String] The reason the for the creation of this role.
|
3298
|
-
# @return [Role] the created role.
|
3299
|
-
def create_role(name: 'new role', colour: 0, hoist: false, mentionable: false, permissions: 104_324_161, reason: nil)
|
3300
|
-
colour = colour.respond_to?(:combined) ? colour.combined : colour
|
3301
|
-
|
3302
|
-
permissions = if permissions.is_a?(Array)
|
3303
|
-
Permissions.bits(permissions)
|
3304
|
-
elsif permissions.respond_to?(:bits)
|
3305
|
-
permissions.bits
|
3306
|
-
else
|
3307
|
-
permissions
|
3308
|
-
end
|
3309
|
-
|
3310
|
-
response = API::Server.create_role(@bot.token, @id, name, colour, hoist, mentionable, permissions, reason)
|
3311
|
-
|
3312
|
-
role = Role.new(JSON.parse(response), @bot, self)
|
3313
|
-
@roles << role
|
3314
|
-
role
|
3315
|
-
end
|
3316
|
-
|
3317
|
-
# @return [Array<ServerBan>] a list of banned users on this server and the reason they were banned.
|
3318
|
-
def bans
|
3319
|
-
response = JSON.parse(API::Server.bans(@bot.token, @id))
|
3320
|
-
response.map do |e|
|
3321
|
-
ServerBan.new(self, User.new(e['user'], @bot), e['reason'])
|
3322
|
-
end
|
3323
|
-
end
|
3324
|
-
|
3325
|
-
# Bans a user from this server.
|
3326
|
-
# @param user [User, #resolve_id] The user to ban.
|
3327
|
-
# @param message_days [Integer] How many days worth of messages sent by the user should be deleted.
|
3328
|
-
# @param reason [String] The reason the user is being banned.
|
3329
|
-
def ban(user, message_days = 0, reason: nil)
|
3330
|
-
API::Server.ban_user(@bot.token, @id, user.resolve_id, message_days, reason)
|
3331
|
-
end
|
3332
|
-
|
3333
|
-
# Unbans a previously banned user from this server.
|
3334
|
-
# @param user [User, #resolve_id] The user to unban.
|
3335
|
-
# @param reason [String] The reason the user is being unbanned.
|
3336
|
-
def unban(user, reason = nil)
|
3337
|
-
API::Server.unban_user(@bot.token, @id, user.resolve_id, reason)
|
3338
|
-
end
|
3339
|
-
|
3340
|
-
# Kicks a user from this server.
|
3341
|
-
# @param user [User, #resolve_id] The user to kick.
|
3342
|
-
# @param reason [String] The reason the user is being kicked.
|
3343
|
-
def kick(user, reason = nil)
|
3344
|
-
API::Server.remove_member(@bot.token, @id, user.resolve_id, reason)
|
3345
|
-
end
|
3346
|
-
|
3347
|
-
# Forcibly moves a user into a different voice channel. Only works if the bot has the permission needed.
|
3348
|
-
# @param user [User, #resolve_id] The user to move.
|
3349
|
-
# @param channel [Channel, #resolve_id] The voice channel to move into.
|
3350
|
-
def move(user, channel)
|
3351
|
-
API::Server.update_member(@bot.token, @id, user.resolve_id, channel_id: channel.resolve_id)
|
3352
|
-
end
|
3353
|
-
|
3354
|
-
# Deletes this server. Be aware that this is permanent and impossible to undo, so be careful!
|
3355
|
-
def delete
|
3356
|
-
API::Server.delete(@bot.token, @id)
|
3357
|
-
end
|
3358
|
-
|
3359
|
-
# Leave the server.
|
3360
|
-
def leave
|
3361
|
-
API::User.leave_server(@bot.token, @id)
|
3362
|
-
end
|
3363
|
-
|
3364
|
-
# Transfers server ownership to another user.
|
3365
|
-
# @param user [User, #resolve_id] The user who should become the new owner.
|
3366
|
-
def owner=(user)
|
3367
|
-
API::Server.transfer_ownership(@bot.token, @id, user.resolve_id)
|
3368
|
-
end
|
3369
|
-
|
3370
|
-
# Sets the server's name.
|
3371
|
-
# @param name [String] The new server name.
|
3372
|
-
def name=(name)
|
3373
|
-
update_server_data(name: name)
|
3374
|
-
end
|
3375
|
-
|
3376
|
-
# @return [Array<VoiceRegion>] collection of available voice regions to this guild
|
3377
|
-
def available_voice_regions
|
3378
|
-
return @available_voice_regions if @available_voice_regions
|
3379
|
-
|
3380
|
-
@available_voice_regions = {}
|
3381
|
-
|
3382
|
-
data = JSON.parse API::Server.regions(@bot.token, @id)
|
3383
|
-
@available_voice_regions = data.map { |e| VoiceRegion.new e }
|
3384
|
-
end
|
3385
|
-
|
3386
|
-
# @return [VoiceRegion, nil] voice region data for this server's region
|
3387
|
-
# @note This may return `nil` if this server's voice region is deprecated.
|
3388
|
-
def region
|
3389
|
-
available_voice_regions.find { |e| e.id == @region_id }
|
3390
|
-
end
|
3391
|
-
|
3392
|
-
# Moves the server to another region. This will cause a voice interruption of at most a second.
|
3393
|
-
# @param region [String] The new region the server should be in.
|
3394
|
-
def region=(region)
|
3395
|
-
update_server_data(region: region.to_s)
|
3396
|
-
end
|
3397
|
-
|
3398
|
-
# Sets the server's icon.
|
3399
|
-
# @param icon [String, #read] The new icon, in base64-encoded JPG format.
|
3400
|
-
def icon=(icon)
|
3401
|
-
if icon.respond_to? :read
|
3402
|
-
icon_string = 'data:image/jpg;base64,'
|
3403
|
-
icon_string += Base64.strict_encode64(icon.read)
|
3404
|
-
update_server_data(icon_id: icon_string)
|
3405
|
-
else
|
3406
|
-
update_server_data(icon_id: icon)
|
3407
|
-
end
|
3408
|
-
end
|
3409
|
-
|
3410
|
-
# Sets the server's AFK channel.
|
3411
|
-
# @param afk_channel [Channel, nil] The new AFK channel, or `nil` if there should be none set.
|
3412
|
-
def afk_channel=(afk_channel)
|
3413
|
-
update_server_data(afk_channel_id: afk_channel.resolve_id)
|
3414
|
-
end
|
3415
|
-
|
3416
|
-
# Sets the server's system channel.
|
3417
|
-
# @param system_channel [Channel, String, Integer, #resolve_id, nil] The new system channel, or `nil` should it be disabled.
|
3418
|
-
def system_channel=(system_channel)
|
3419
|
-
update_server_data(system_channel_id: system_channel.resolve_id)
|
3420
|
-
end
|
3421
|
-
|
3422
|
-
# Sets the amount of time after which a user gets moved into the AFK channel.
|
3423
|
-
# @param afk_timeout [Integer] The AFK timeout, in seconds.
|
3424
|
-
def afk_timeout=(afk_timeout)
|
3425
|
-
update_server_data(afk_timeout: afk_timeout)
|
3426
|
-
end
|
3427
|
-
|
3428
|
-
# A map of possible server verification levels to symbol names
|
3429
|
-
VERIFICATION_LEVELS = {
|
3430
|
-
none: 0,
|
3431
|
-
low: 1,
|
3432
|
-
medium: 2,
|
3433
|
-
high: 3,
|
3434
|
-
very_high: 4
|
3435
|
-
}.freeze
|
3436
|
-
|
3437
|
-
# @return [Symbol] the verification level of the server (:none = none, :low = 'Must have a verified email on their Discord account', :medium = 'Has to be registered with Discord for at least 5 minutes', :high = 'Has to be a member of this server for at least 10 minutes', :very_high = 'Must have a verified phone on their Discord account').
|
3438
|
-
def verification_level
|
3439
|
-
VERIFICATION_LEVELS.key @verification_level
|
3440
|
-
end
|
3441
|
-
|
3442
|
-
# Sets the verification level of the server
|
3443
|
-
# @param level [Integer, Symbol] The verification level from 0-4 or Symbol (see {VERIFICATION_LEVELS})
|
3444
|
-
def verification_level=(level)
|
3445
|
-
level = VERIFICATION_LEVELS[level] if level.is_a?(Symbol)
|
3446
|
-
|
3447
|
-
update_server_data(verification_level: level)
|
3448
|
-
end
|
3449
|
-
|
3450
|
-
# A map of possible message notification levels to symbol names
|
3451
|
-
NOTIFICATION_LEVELS = {
|
3452
|
-
all_messages: 0,
|
3453
|
-
only_mentions: 1
|
3454
|
-
}.freeze
|
3455
|
-
|
3456
|
-
# @return [Symbol] the default message notifications settings of the server (:all = 'All messages', :mentions = 'Only @mentions').
|
3457
|
-
def default_message_notifications
|
3458
|
-
NOTIFICATION_LEVELS.key @default_message_notifications
|
3459
|
-
end
|
3460
|
-
|
3461
|
-
# Sets the default message notification level
|
3462
|
-
# @param notification_level [Integer, Symbol] The default message notificiation 0-1 or Symbol (see {NOTIFICATION_LEVELS})
|
3463
|
-
def default_message_notifications=(notification_level)
|
3464
|
-
notification_level = NOTIFICATION_LEVELS[notification_level] if notification_level.is_a?(Symbol)
|
3465
|
-
|
3466
|
-
update_server_data(default_message_notifications: notification_level)
|
3467
|
-
end
|
3468
|
-
|
3469
|
-
alias_method :notification_level=, :default_message_notifications=
|
3470
|
-
|
3471
|
-
# Sets the server splash
|
3472
|
-
# @param splash_hash [String] The splash hash
|
3473
|
-
def splash=(splash_hash)
|
3474
|
-
update_server_data(splash: splash_hash)
|
3475
|
-
end
|
3476
|
-
|
3477
|
-
# A map of possible content filter levels to symbol names
|
3478
|
-
FILTER_LEVELS = {
|
3479
|
-
disabled: 0,
|
3480
|
-
members_without_roles: 1,
|
3481
|
-
all_members: 2
|
3482
|
-
}.freeze
|
3483
|
-
|
3484
|
-
# @return [Symbol] the explicit content filter level of the server (:none = 'Don't scan any messages.', :exclude_roles = 'Scan messages for members without a role.', :all = 'Scan messages sent by all members.').
|
3485
|
-
def explicit_content_filter
|
3486
|
-
FILTER_LEVELS.key @explicit_content_filter
|
3487
|
-
end
|
3488
|
-
|
3489
|
-
alias_method :content_filter_level, :explicit_content_filter
|
3490
|
-
|
3491
|
-
# Sets the server content filter.
|
3492
|
-
# @param filter_level [Integer, Symbol] The content filter from 0-2 or Symbol (see {FILTER_LEVELS})
|
3493
|
-
def explicit_content_filter=(filter_level)
|
3494
|
-
filter_level = FILTER_LEVELS[filter_level] if filter_level.is_a?(Symbol)
|
3495
|
-
|
3496
|
-
update_server_data(explicit_content_filter: filter_level)
|
3497
|
-
end
|
3498
|
-
|
3499
|
-
# @return [true, false] whether this server has any emoji or not.
|
3500
|
-
def any_emoji?
|
3501
|
-
@emoji.any?
|
3502
|
-
end
|
3503
|
-
|
3504
|
-
alias_method :has_emoji?, :any_emoji?
|
3505
|
-
alias_method :emoji?, :any_emoji?
|
3506
|
-
|
3507
|
-
# Requests a list of Webhooks on the server.
|
3508
|
-
# @return [Array<Webhook>] webhooks on the server.
|
3509
|
-
def webhooks
|
3510
|
-
webhooks = JSON.parse(API::Server.webhooks(@bot.token, @id))
|
3511
|
-
webhooks.map { |webhook| Webhook.new(webhook, @bot) }
|
3512
|
-
end
|
3513
|
-
|
3514
|
-
# Requests a list of Invites to the server.
|
3515
|
-
# @return [Array<Invite>] invites to the server.
|
3516
|
-
def invites
|
3517
|
-
invites = JSON.parse(API::Server.invites(@bot.token, @id))
|
3518
|
-
invites.map { |invite| Invite.new(invite, @bot) }
|
3519
|
-
end
|
3520
|
-
|
3521
|
-
# Processes a GUILD_MEMBERS_CHUNK packet, specifically the members field
|
3522
|
-
# @note For internal use only
|
3523
|
-
# @!visibility private
|
3524
|
-
def process_chunk(members)
|
3525
|
-
process_members(members)
|
3526
|
-
@processed_chunk_members += members.length
|
3527
|
-
LOGGER.debug("Processed one chunk on server #{@id} - length #{members.length}")
|
3528
|
-
|
3529
|
-
# Don't bother with the rest of the method if it's not truly the last packet
|
3530
|
-
return unless @processed_chunk_members == @member_count
|
3531
|
-
|
3532
|
-
LOGGER.debug("Finished chunking server #{@id}")
|
3533
|
-
|
3534
|
-
# Reset everything to normal
|
3535
|
-
@chunked = true
|
3536
|
-
@processed_chunk_members = 0
|
3537
|
-
end
|
3538
|
-
|
3539
|
-
# @return [Channel, nil] the AFK voice channel of this server, or `nil` if none is set.
|
3540
|
-
def afk_channel
|
3541
|
-
@bot.channel(@afk_channel_id) if @afk_channel_id
|
3542
|
-
end
|
3543
|
-
|
3544
|
-
# @return [Channel, nil] the system channel (used for automatic welcome messages) of a server, or `nil` if none is set.
|
3545
|
-
def system_channel
|
3546
|
-
@bot.channel(@system_channel_id) if @system_channel_id
|
3547
|
-
end
|
3548
|
-
|
3549
|
-
# Updates the cached data with new data
|
3550
|
-
# @note For internal use only
|
3551
|
-
# @!visibility private
|
3552
|
-
def update_data(new_data = nil)
|
3553
|
-
new_data ||= JSON.parse(API::Server.resolve(@bot.token, @id))
|
3554
|
-
@name = new_data[:name] || new_data['name'] || @name
|
3555
|
-
@region_id = new_data[:region] || new_data['region'] || @region_id
|
3556
|
-
@icon_id = new_data[:icon] || new_data['icon'] || @icon_id
|
3557
|
-
@afk_timeout = new_data[:afk_timeout] || new_data['afk_timeout'] || @afk_timeout
|
3558
|
-
|
3559
|
-
afk_channel_id = new_data[:afk_channel_id] || new_data['afk_channel_id'] || @afk_channel
|
3560
|
-
@afk_channel_id = afk_channel_id.nil? ? nil : afk_channel_id.resolve_id
|
3561
|
-
embed_channel_id = new_data[:embed_channel_id] || new_data['embed_channel_id'] || @embed_channel
|
3562
|
-
@embed_channel_id = embed_channel_id.nil? ? nil : embed_channel_id.resolve_id
|
3563
|
-
system_channel_id = new_data[:system_channel_id] || new_data['system_channel_id'] || @system_channel
|
3564
|
-
@system_channel_id = system_channel_id.nil? ? nil : system_channel_id.resolve_id
|
3565
|
-
|
3566
|
-
@embed_enabled = new_data[:embed_enabled] || new_data['embed_enabled']
|
3567
|
-
@splash = new_data[:splash_id] || new_data['splash_id'] || @splash_id
|
3568
|
-
|
3569
|
-
@verification_level = new_data[:verification_level] || new_data['verification_level'] || @verification_level
|
3570
|
-
@explicit_content_filter = new_data[:explicit_content_filter] || new_data['explicit_content_filter'] || @explicit_content_filter
|
3571
|
-
@default_message_notifications = new_data[:default_message_notifications] || new_data['default_message_notifications'] || @default_message_notifications
|
3572
|
-
end
|
3573
|
-
|
3574
|
-
# Adds a channel to this server's cache
|
3575
|
-
# @note For internal use only
|
3576
|
-
# @!visibility private
|
3577
|
-
def add_channel(channel)
|
3578
|
-
@channels << channel
|
3579
|
-
@channels_by_id[channel.id] = channel
|
3580
|
-
end
|
3581
|
-
|
3582
|
-
# Deletes a channel from this server's cache
|
3583
|
-
# @note For internal use only
|
3584
|
-
# @!visibility private
|
3585
|
-
def delete_channel(id)
|
3586
|
-
@channels.reject! { |e| e.id == id }
|
3587
|
-
@channels_by_id.delete(id)
|
3588
|
-
end
|
3589
|
-
|
3590
|
-
# Updates the cached emoji data with new data
|
3591
|
-
# @note For internal use only
|
3592
|
-
# @!visibility private
|
3593
|
-
def update_emoji_data(new_data)
|
3594
|
-
@emoji = {}
|
3595
|
-
process_emoji(new_data['emojis'])
|
3596
|
-
end
|
3597
|
-
|
3598
|
-
# The inspect method is overwritten to give more useful output
|
3599
|
-
def inspect
|
3600
|
-
"<Server name=#{@name} id=#{@id} large=#{@large} region=#{@region} owner=#{@owner} afk_channel_id=#{@afk_channel_id} system_channel_id=#{@system_channel_id} afk_timeout=#{@afk_timeout}>"
|
3601
|
-
end
|
3602
|
-
|
3603
|
-
private
|
3604
|
-
|
3605
|
-
def update_server_data(new_data)
|
3606
|
-
response = JSON.parse(API::Server.update(@bot.token, @id,
|
3607
|
-
new_data[:name] || @name,
|
3608
|
-
new_data[:region] || @region_id,
|
3609
|
-
new_data[:icon_id] || @icon_id,
|
3610
|
-
new_data[:afk_channel_id] || @afk_channel_id,
|
3611
|
-
new_data[:afk_timeout] || @afk_timeout,
|
3612
|
-
new_data[:splash] || @splash,
|
3613
|
-
new_data[:default_message_notifications] || @default_message_notifications,
|
3614
|
-
new_data[:verification_level] || @verification_level,
|
3615
|
-
new_data[:explicit_content_filter] || @explicit_content_filter,
|
3616
|
-
new_data[:system_channel_id] || @system_channel_id))
|
3617
|
-
update_data(response)
|
3618
|
-
end
|
3619
|
-
|
3620
|
-
def process_roles(roles)
|
3621
|
-
# Create roles
|
3622
|
-
@roles = []
|
3623
|
-
@roles_by_id = {}
|
3624
|
-
|
3625
|
-
return unless roles
|
3626
|
-
roles.each do |element|
|
3627
|
-
role = Role.new(element, @bot, self)
|
3628
|
-
@roles << role
|
3629
|
-
@roles_by_id[role.id] = role
|
3630
|
-
end
|
3631
|
-
end
|
3632
|
-
|
3633
|
-
def process_emoji(emoji)
|
3634
|
-
return if emoji.empty?
|
3635
|
-
emoji.each do |element|
|
3636
|
-
new_emoji = Emoji.new(element, @bot, self)
|
3637
|
-
@emoji[new_emoji.id] = new_emoji
|
3638
|
-
end
|
3639
|
-
end
|
3640
|
-
|
3641
|
-
def process_members(members)
|
3642
|
-
return unless members
|
3643
|
-
members.each do |element|
|
3644
|
-
member = Member.new(element, self, @bot)
|
3645
|
-
@members[member.id] = member
|
3646
|
-
end
|
3647
|
-
end
|
3648
|
-
|
3649
|
-
def process_presences(presences)
|
3650
|
-
# Update user statuses with presence info
|
3651
|
-
return unless presences
|
3652
|
-
presences.each do |element|
|
3653
|
-
next unless element['user']
|
3654
|
-
user_id = element['user']['id'].to_i
|
3655
|
-
user = @members[user_id]
|
3656
|
-
if user
|
3657
|
-
user.update_presence(element)
|
3658
|
-
else
|
3659
|
-
LOGGER.warn "Rogue presence update! #{element['user']['id']} on #{@id}"
|
3660
|
-
end
|
3661
|
-
end
|
3662
|
-
end
|
3663
|
-
|
3664
|
-
def process_channels(channels)
|
3665
|
-
@channels = []
|
3666
|
-
@channels_by_id = {}
|
3667
|
-
|
3668
|
-
return unless channels
|
3669
|
-
channels.each do |element|
|
3670
|
-
channel = @bot.ensure_channel(element, self)
|
3671
|
-
@channels << channel
|
3672
|
-
@channels_by_id[channel.id] = channel
|
3673
|
-
end
|
3674
|
-
end
|
3675
|
-
|
3676
|
-
def process_voice_states(voice_states)
|
3677
|
-
return unless voice_states
|
3678
|
-
voice_states.each do |element|
|
3679
|
-
update_voice_state(element)
|
3680
|
-
end
|
3681
|
-
end
|
3682
|
-
end
|
3683
|
-
|
3684
|
-
# A ban entry on a server
|
3685
|
-
class ServerBan
|
3686
|
-
# @return [String, nil] the reason the user was banned, if provided
|
3687
|
-
attr_reader :reason
|
3688
|
-
|
3689
|
-
# @return [User] the user that was banned
|
3690
|
-
attr_reader :user
|
3691
|
-
|
3692
|
-
# @return [Server] the server this ban belongs to
|
3693
|
-
attr_reader :server
|
3694
|
-
|
3695
|
-
# @!visibility private
|
3696
|
-
def initialize(server, user, reason)
|
3697
|
-
@server = server
|
3698
|
-
@user = user
|
3699
|
-
@reason = reason
|
3700
|
-
end
|
3701
|
-
|
3702
|
-
# Removes this ban on the associated user in the server
|
3703
|
-
# @param reason [String] the reason for removing the ban
|
3704
|
-
def remove(reason = nil)
|
3705
|
-
@server.unban(user, reason)
|
3706
|
-
end
|
3707
|
-
|
3708
|
-
alias_method :unban, :remove
|
3709
|
-
alias_method :lift, :remove
|
3710
|
-
end
|
3711
|
-
|
3712
|
-
# A webhook on a server channel
|
3713
|
-
class Webhook
|
3714
|
-
include IDObject
|
3715
|
-
|
3716
|
-
# @return [String] the webhook name.
|
3717
|
-
attr_reader :name
|
3718
|
-
|
3719
|
-
# @return [Channel] the channel that the webhook is currently connected to.
|
3720
|
-
attr_reader :channel
|
3721
|
-
|
3722
|
-
# @return [Server] the server that the webhook is currently connected to.
|
3723
|
-
attr_reader :server
|
3724
|
-
|
3725
|
-
# @return [String] the webhook's token.
|
3726
|
-
attr_reader :token
|
3727
|
-
|
3728
|
-
# @return [String] the webhook's avatar id.
|
3729
|
-
attr_reader :avatar
|
3730
|
-
|
3731
|
-
# Gets the user object of the creator of the webhook. May be limited to username, discriminator,
|
3732
|
-
# ID and avatar if the bot cannot reach the owner
|
3733
|
-
# @return [Member, User, nil] the user object of the owner or nil if the webhook was requested using the token.
|
3734
|
-
attr_reader :owner
|
3735
|
-
|
3736
|
-
def initialize(data, bot)
|
3737
|
-
@bot = bot
|
3738
|
-
|
3739
|
-
@name = data['name']
|
3740
|
-
@id = data['id'].to_i
|
3741
|
-
@channel = bot.channel(data['channel_id'])
|
3742
|
-
@server = @channel.server
|
3743
|
-
@token = data['token']
|
3744
|
-
@avatar = data['avatar']
|
3745
|
-
|
3746
|
-
# Will not exist if the data was requested through a webhook token
|
3747
|
-
return unless data['user']
|
3748
|
-
@owner = @server.member(data['user']['id'].to_i)
|
3749
|
-
return if @owner
|
3750
|
-
Discordrb::LOGGER.debug("Member with ID #{data['user']['id']} not cached (possibly left the server).")
|
3751
|
-
@owner = @bot.ensure_user(data['user'])
|
3752
|
-
end
|
3753
|
-
|
3754
|
-
# Sets the webhook's avatar.
|
3755
|
-
# @param avatar [String, #read] The new avatar, in base64-encoded JPG format.
|
3756
|
-
def avatar=(avatar)
|
3757
|
-
update_webhook(avatar: avatarise(avatar))
|
3758
|
-
end
|
3759
|
-
|
3760
|
-
# Deletes the webhook's avatar.
|
3761
|
-
def delete_avatar
|
3762
|
-
update_webhook(avatar: nil)
|
3763
|
-
end
|
3764
|
-
|
3765
|
-
# Sets the webhook's channel
|
3766
|
-
# @param channel [Channel, String, Integer, #resolve_id] The channel the webhook should use.
|
3767
|
-
def channel=(channel)
|
3768
|
-
update_webhook(channel_id: channel.resolve_id)
|
3769
|
-
end
|
3770
|
-
|
3771
|
-
# Sets the webhook's name.
|
3772
|
-
# @param name [String] The webhook's new name.
|
3773
|
-
def name=(name)
|
3774
|
-
update_webhook(name: name)
|
3775
|
-
end
|
3776
|
-
|
3777
|
-
# Updates the webhook if you need to edit more than 1 attribute.
|
3778
|
-
# @param data [Hash] the data to update.
|
3779
|
-
# @option data [String, #read, nil] :avatar The new avatar, in base64-encoded JPG format, or nil to delete the avatar.
|
3780
|
-
# @option data [Channel, String, Integer, #resolve_id] :channel The channel the webhook should use.
|
3781
|
-
# @option data [String] :name The webhook's new name.
|
3782
|
-
# @option data [String] :reason The reason for the webhook changes.
|
3783
|
-
def update(data)
|
3784
|
-
# Only pass a value for avatar if the key is defined as sending nil will delete the
|
3785
|
-
data[:avatar] = avatarise(data[:avatar]) if data.key?(:avatar)
|
3786
|
-
data[:channel_id] = data[:channel].resolve_id
|
3787
|
-
data.delete(:channel)
|
3788
|
-
update_webhook(data)
|
3789
|
-
end
|
3790
|
-
|
3791
|
-
# Deletes the webhook.
|
3792
|
-
# @param reason [String] The reason the invite is being deleted.
|
3793
|
-
def delete(reason = nil)
|
3794
|
-
if token?
|
3795
|
-
API::Webhook.token_delete_webhook(@token, @id, reason)
|
3796
|
-
else
|
3797
|
-
API::Webhook.delete_webhook(@bot.token, @id, reason)
|
3798
|
-
end
|
3799
|
-
end
|
3800
|
-
|
3801
|
-
# Utility function to get a webhook's avatar URL.
|
3802
|
-
# @return [String] the URL to the avatar image
|
3803
|
-
def avatar_url
|
3804
|
-
return API::User.default_avatar unless @avatar
|
3805
|
-
API::User.avatar_url(@id, @avatar)
|
3806
|
-
end
|
3807
|
-
|
3808
|
-
# The `inspect` method is overwritten to give more useful output.
|
3809
|
-
def inspect
|
3810
|
-
"<Webhook name=#{@name} id=#{@id}>"
|
3811
|
-
end
|
3812
|
-
|
3813
|
-
# Utility function to know if the webhook was requested through a webhook token, rather than auth.
|
3814
|
-
# @return [true, false] whether the webhook was requested by token or not.
|
3815
|
-
def token?
|
3816
|
-
@owner.nil?
|
3817
|
-
end
|
3818
|
-
|
3819
|
-
private
|
3820
|
-
|
3821
|
-
def avatarise(avatar)
|
3822
|
-
if avatar.respond_to? :read
|
3823
|
-
"data:image/jpg;base64,#{Base64.strict_encode64(avatar.read)}"
|
3824
|
-
else
|
3825
|
-
avatar
|
3826
|
-
end
|
3827
|
-
end
|
3828
|
-
|
3829
|
-
def update_internal(data)
|
3830
|
-
@name = data['name']
|
3831
|
-
@avatar_id = data['avatar']
|
3832
|
-
@channel = @bot.channel(data['channel_id'])
|
3833
|
-
end
|
3834
|
-
|
3835
|
-
def update_webhook(new_data)
|
3836
|
-
reason = new_data.delete(:reason)
|
3837
|
-
data = JSON.parse(if token?
|
3838
|
-
API::Webhook.token_update_webhook(@token, @id, new_data, reason)
|
3839
|
-
else
|
3840
|
-
API::Webhook.update_webhook(@bot.token, @id, new_data, reason)
|
3841
|
-
end)
|
3842
|
-
# Only update cache if API call worked
|
3843
|
-
update_internal(data) if data['name']
|
3844
|
-
end
|
3845
|
-
end
|
3846
|
-
|
3847
|
-
# A server's audit logs
|
3848
|
-
class AuditLogs
|
3849
|
-
# The numbers associated with the type of action.
|
3850
|
-
Actions = {
|
3851
|
-
1 => :server_update,
|
3852
|
-
10 => :channel_create,
|
3853
|
-
11 => :channel_update,
|
3854
|
-
12 => :channel_delete,
|
3855
|
-
13 => :channel_overwrite_create,
|
3856
|
-
14 => :channel_overwrite_update,
|
3857
|
-
15 => :channel_overwrite_delete,
|
3858
|
-
20 => :member_kick,
|
3859
|
-
21 => :member_prune,
|
3860
|
-
22 => :member_ban_add,
|
3861
|
-
23 => :member_ban_remove,
|
3862
|
-
24 => :member_update,
|
3863
|
-
25 => :member_role_update,
|
3864
|
-
30 => :role_create,
|
3865
|
-
31 => :role_update,
|
3866
|
-
32 => :role_delete,
|
3867
|
-
40 => :invite_create,
|
3868
|
-
41 => :invite_update,
|
3869
|
-
42 => :invite_delete,
|
3870
|
-
50 => :webhook_create,
|
3871
|
-
51 => :webhook_update,
|
3872
|
-
52 => :webhook_delete,
|
3873
|
-
60 => :emoji_create,
|
3874
|
-
61 => :emoji_update,
|
3875
|
-
62 => :emoji_delete,
|
3876
|
-
# 70
|
3877
|
-
# 71
|
3878
|
-
72 => :message_delete
|
3879
|
-
}.freeze
|
3880
|
-
|
3881
|
-
# @return [Hash<String => User>] the users included in the audit logs.
|
3882
|
-
attr_reader :users
|
3883
|
-
|
3884
|
-
# @return [Hash<String => Webhook>] the webhooks included in the audit logs.
|
3885
|
-
attr_reader :webhooks
|
3886
|
-
|
3887
|
-
# @return [Array<Entry>] the entries listed in the audit logs.
|
3888
|
-
attr_reader :entries
|
3889
|
-
|
3890
|
-
# @!visibility private
|
3891
|
-
def initialize(server, bot, data)
|
3892
|
-
@bot = bot
|
3893
|
-
@server = server
|
3894
|
-
@users = {}
|
3895
|
-
@webhooks = {}
|
3896
|
-
@entries = data['audit_log_entries'].map { |entry| Entry.new(self, @server, @bot, entry) }
|
3897
|
-
|
3898
|
-
process_users(data['users'])
|
3899
|
-
process_webhooks(data['webhooks'])
|
3900
|
-
end
|
3901
|
-
|
3902
|
-
# An entry in a server's audit logs.
|
3903
|
-
class Entry
|
3904
|
-
include IDObject
|
3905
|
-
|
3906
|
-
# @return [Symbol] the action that was performed.
|
3907
|
-
attr_reader :action
|
3908
|
-
|
3909
|
-
# @return [Symbol] the type action that was performed. (:create, :delete, :update, :unknown)
|
3910
|
-
attr_reader :action_type
|
3911
|
-
|
3912
|
-
# @return [Symbol] the type of target being performed on. (:server, :channel, :user, :role, :invite, :webhook, :emoji, :unknown)
|
3913
|
-
attr_reader :target_type
|
3914
|
-
|
3915
|
-
# @return [Integer, nil] the amount of messages deleted. Only present if the action is `:message_delete`.
|
3916
|
-
attr_reader :count
|
3917
|
-
alias_method :amount, :count
|
3918
|
-
|
3919
|
-
# @return [Integer, nil] the amount of days the members were inactive for. Only present if the action is `:member_prune`.
|
3920
|
-
attr_reader :days
|
3921
|
-
|
3922
|
-
# @return [Integer, nil] the amount of members removed. Only present if the action is `:member_prune`.
|
3923
|
-
attr_reader :members_removed
|
3924
|
-
|
3925
|
-
# @return [String, nil] the reason for this action occuring.
|
3926
|
-
attr_reader :reason
|
3927
|
-
|
3928
|
-
# @return [Hash<String => Change>, RoleChange, nil] the changes from this log, listing the key as the key changed. Will be a RoleChange object if the action is `:member_role_update`. Will be nil if the action is either `:message_delete` or `:member_prune`.
|
3929
|
-
attr_reader :changes
|
3930
|
-
|
3931
|
-
# @!visibility private
|
3932
|
-
def initialize(logs, server, bot, data)
|
3933
|
-
@bot = bot
|
3934
|
-
@id = data['id'].resolve_id
|
3935
|
-
@logs = logs
|
3936
|
-
@server = server
|
3937
|
-
@data = data
|
3938
|
-
@action = Actions[data['action_type']]
|
3939
|
-
@reason = data['reason']
|
3940
|
-
@action_type = AuditLogs.action_type_for(data['action_type'])
|
3941
|
-
@target_type = AuditLogs.target_type_for(data['action_type'])
|
3942
|
-
|
3943
|
-
# Sets the 'changes' variable to a empty hash if there are no special actions.
|
3944
|
-
@changes = {} unless @action == :message_delete || @action == :member_prune || @action == :member_role_update
|
3945
|
-
|
3946
|
-
# Sets the 'changes' variable to a RoleChange class if theres a role update.
|
3947
|
-
@changes = RoleChange.new(data['changes'][0], @server) if @action == :member_role_update
|
3948
|
-
|
3949
|
-
process_changes(data['changes']) unless @action == :member_role_update
|
3950
|
-
return unless data.include?('options')
|
3951
|
-
|
3952
|
-
# Checks and sets variables for special action options.
|
3953
|
-
@count = data['options']['count'].to_i unless data['options']['count'].nil?
|
3954
|
-
@channel_id = data['options']['channel'].to_i unless data['options']['channel'].nil?
|
3955
|
-
@days = data['options']['delete_member_days'].to_i unless data['options']['delete_member_days'].nil?
|
3956
|
-
@members_removed = data['options']['members_removed'].to_i unless data['options']['members_removed'].nil?
|
3957
|
-
end
|
3958
|
-
|
3959
|
-
# @return [Server, Channel, Member, User, Role, Invite, Webhook, Emoji, nil] the target being performed on.
|
3960
|
-
def target
|
3961
|
-
@target ||= process_target(@data['target_id'], @target_type)
|
3962
|
-
end
|
3963
|
-
|
3964
|
-
# @return [Member, User] the user that authored this action. Can be a User object if the user no longer exists in the server.
|
3965
|
-
def user
|
3966
|
-
@user ||= @server.member(@data['user_id'].to_i) || @bot.user(@data['user_id'].to_i) || @logs.user(@data['user_id'].to_i)
|
3967
|
-
end
|
3968
|
-
alias_method :author, :user
|
3969
|
-
|
3970
|
-
# @return [Channel, nil] the amount of messages deleted. Won't be nil if the action is `:message_delete`.
|
3971
|
-
def channel
|
3972
|
-
return nil unless @channel_id
|
3973
|
-
@channel ||= @bot.channel(@channel_id, @server, bot, self)
|
3974
|
-
end
|
3975
|
-
|
3976
|
-
# @!visibility private
|
3977
|
-
def process_target(id, type)
|
3978
|
-
id = id.resolve_id unless id.nil?
|
3979
|
-
case type
|
3980
|
-
when :server then @server # Since it won't be anything else
|
3981
|
-
when :channel then @bot.channel(id, @server)
|
3982
|
-
when :user, :message then @server.member(id) || @bot.user(id) || @logs.user(id)
|
3983
|
-
when :role then @server.role(id)
|
3984
|
-
when :invite then @bot.invite(@data['changes'].find { |change| change['key'] == 'code' }.values.delete_if { |v| v == 'code' }.first)
|
3985
|
-
when :webhook then @server.webhooks.find { |webhook| webhook.id == id } || @logs.webhook(id)
|
3986
|
-
when :emoji then @server.emoji[id]
|
3987
|
-
end
|
3988
|
-
end
|
3989
|
-
|
3990
|
-
# The inspect method is overwritten to give more useful output
|
3991
|
-
def inspect
|
3992
|
-
"<AuditLogs::Entry id=#{@id} action=#{@action} reason=#{@reason} action_type=#{@action_type} target_type=#{@target_type} count=#{@count} days=#{@days} members_removed=#{@members_removed}>"
|
3993
|
-
end
|
3994
|
-
|
3995
|
-
# Process action changes
|
3996
|
-
# @note For internal use only
|
3997
|
-
# @!visibility private
|
3998
|
-
def process_changes(changes)
|
3999
|
-
return unless changes
|
4000
|
-
changes.each do |element|
|
4001
|
-
change = Change.new(element, @server, @bot, self)
|
4002
|
-
@changes[change.key] = change
|
4003
|
-
end
|
4004
|
-
end
|
4005
|
-
end
|
4006
|
-
|
4007
|
-
# A change in a audit log entry.
|
4008
|
-
class Change
|
4009
|
-
# @return [String] the key that was changed.
|
4010
|
-
# @note You should check with the Discord API Documentation on what key gives out what value.
|
4011
|
-
attr_reader :key
|
4012
|
-
|
4013
|
-
# @return [String, Integer, true, false, Permissions, Overwrite, nil] the value that was changed from.
|
4014
|
-
attr_reader :old
|
4015
|
-
alias_method :old_value, :old
|
4016
|
-
|
4017
|
-
# @return [String, Integer, true, false, Permissions, Overwrite, nil] the value that was changed to.
|
4018
|
-
attr_reader :new
|
4019
|
-
alias_method :new_value, :new
|
4020
|
-
|
4021
|
-
# @!visibility private
|
4022
|
-
def initialize(data, server, bot, logs)
|
4023
|
-
@key = data['key']
|
4024
|
-
@old = data['old_value']
|
4025
|
-
@new = data['new_value']
|
4026
|
-
@server = server
|
4027
|
-
@bot = bot
|
4028
|
-
@logs = logs
|
4029
|
-
|
4030
|
-
@old = Permissions.new(@old) if @old && @key == 'permissions'
|
4031
|
-
@new = Permissions.new(@new) if @new && @key == 'permissions'
|
4032
|
-
|
4033
|
-
@old = @old.map { |o| Overwrite.new(o['id'], type: o['type'].to_sym, allow: o['allow'], deny: o['deny']) } if @old && @key == 'permission_overwrites'
|
4034
|
-
@new = @new.map { |o| Overwrite.new(o['id'], type: o['type'].to_sym, allow: o['allow'], deny: o['deny']) } if @new && @key == 'permission_overwrites'
|
4035
|
-
end
|
4036
|
-
|
4037
|
-
# @return [Channel, nil] the channel that was previously used in the server widget. Only present if the key for this change is `widget_channel_id`.
|
4038
|
-
def old_widget_channel
|
4039
|
-
@bot.channel(@old, @server) if @old && @key == 'widget_channel_id'
|
4040
|
-
end
|
4041
|
-
|
4042
|
-
# @return [Channel, nil] the channel that is used in the server widget prior to this change. Only present if the key for this change is `widget_channel_id`.
|
4043
|
-
def new_widget_channel
|
4044
|
-
@bot.channel(@new, @server) if @new && @key == 'widget_channel_id'
|
4045
|
-
end
|
4046
|
-
|
4047
|
-
# @return [Channel, nil] the channel that was previously used in the server as an AFK channel. Only present if the key for this change is `afk_channel_id`.
|
4048
|
-
def old_afk_channel
|
4049
|
-
@bot.channel(@old, @server) if @old && @key == 'afk_channel_id'
|
4050
|
-
end
|
4051
|
-
|
4052
|
-
# @return [Channel, nil] the channel that is used in the server as an AFK channel prior to this change. Only present if the key for this change is `afk_channel_id`.
|
4053
|
-
def new_afk_channel
|
4054
|
-
@bot.channel(@new, @server) if @new && @key == 'afk_channel_id'
|
4055
|
-
end
|
4056
|
-
|
4057
|
-
# @return [Member, User, nil] the member that used to be the owner of the server. Only present if the for key for this change is `owner_id`.
|
4058
|
-
def old_owner
|
4059
|
-
@server.member(@old) || @bot.user(@old) || @logs.user(@old) if @old && @key == 'owner_id'
|
4060
|
-
end
|
4061
|
-
|
4062
|
-
# @return [Member, User, nil] the member that is now the owner of the server prior to this change. Only present if the key for this change is `owner_id`.
|
4063
|
-
def new_owner
|
4064
|
-
@server.member(@new) || @bot.user(@new) || @logs.user(@new) if @new && @key == 'owner_id'
|
4065
|
-
end
|
4066
|
-
end
|
4067
|
-
|
4068
|
-
# A change that includes roles.
|
4069
|
-
class RoleChange
|
4070
|
-
# @return [Symbol] what type of change this is: (:add, :remove)
|
4071
|
-
attr_reader :type
|
4072
|
-
|
4073
|
-
# @!visibility private
|
4074
|
-
def initialize(data, server)
|
4075
|
-
@type = data['key'].delete('$').to_sym
|
4076
|
-
@role_id = data['new_value'][0]['id'].to_i
|
4077
|
-
@server = server
|
4078
|
-
end
|
4079
|
-
|
4080
|
-
# @return [Role] the role being used.
|
4081
|
-
def role
|
4082
|
-
@role ||= @server.role(@role_id)
|
4083
|
-
end
|
4084
|
-
end
|
4085
|
-
|
4086
|
-
# @return [Entry] the latest entry in the audit logs.
|
4087
|
-
def latest
|
4088
|
-
@entries.first
|
4089
|
-
end
|
4090
|
-
alias_method :first, :latest
|
4091
|
-
|
4092
|
-
# Gets a user in the audit logs data based on user ID
|
4093
|
-
# @note This only uses data given by the audit logs request
|
4094
|
-
# @param id [#resolve_id] The user ID to look for
|
4095
|
-
def user(id)
|
4096
|
-
@users[id.resolve_id]
|
4097
|
-
end
|
4098
|
-
|
4099
|
-
# Gets a webhook in the audit logs data based on webhook ID
|
4100
|
-
# @note This only uses data given by the audit logs request
|
4101
|
-
# @param id [#resolve_id] The webhook ID to look for
|
4102
|
-
def webhook(id)
|
4103
|
-
@webhooks[id.resolve_id]
|
4104
|
-
end
|
4105
|
-
|
4106
|
-
# Process user objects given by the request
|
4107
|
-
# @note For internal use only
|
4108
|
-
# @!visibility private
|
4109
|
-
def process_users(users)
|
4110
|
-
users.each do |element|
|
4111
|
-
user = User.new(element, @bot)
|
4112
|
-
@users[user.id] = user
|
4113
|
-
end
|
4114
|
-
end
|
4115
|
-
|
4116
|
-
# Process webhook objects given by the request
|
4117
|
-
# @note For internal use only
|
4118
|
-
# @!visibility private
|
4119
|
-
def process_webhooks(webhooks)
|
4120
|
-
webhooks.each do |element|
|
4121
|
-
webhook = Webhook.new(element, @bot)
|
4122
|
-
@webhooks[webhook.id] = webhook
|
4123
|
-
end
|
4124
|
-
end
|
4125
|
-
|
4126
|
-
# Find the type of target by it's action number
|
4127
|
-
# @note For internal use only
|
4128
|
-
# @!visibility private
|
4129
|
-
def self.target_type_for(action)
|
4130
|
-
case action
|
4131
|
-
when 1..9 then :server
|
4132
|
-
when 10..19 then :channel
|
4133
|
-
when 20..29 then :user
|
4134
|
-
when 30..39 then :role
|
4135
|
-
when 40..49 then :invite
|
4136
|
-
when 50..59 then :webhook
|
4137
|
-
when 60..69 then :emoji
|
4138
|
-
when 70..79 then :message
|
4139
|
-
else :unknown
|
4140
|
-
end
|
4141
|
-
end
|
4142
|
-
|
4143
|
-
# Find the type of action by its action number
|
4144
|
-
# @note For internal use only
|
4145
|
-
# @!visibility private
|
4146
|
-
def self.action_type_for(action)
|
4147
|
-
action = Actions[action]
|
4148
|
-
return :create if %i[channel_create channel_overwrite_create member_ban_add role_create invite_create webhook_create emoji_create].include?(action)
|
4149
|
-
return :delete if %i[channel_delete channel_overwrite_delete member_kick member_prune member_ban_remove role_delete invite_delete webhook_delete emoji_delete message_delete].include?(action)
|
4150
|
-
return :update if %i[server_update channel_update channel_overwrite_update member_update member_role_update role_update invite_update webhook_update emoji_update].include?(action)
|
4151
|
-
:unknown
|
4152
|
-
end
|
4153
|
-
end
|
4154
|
-
|
4155
|
-
# A colour (red, green and blue values). Used for role colours. If you prefer the American spelling, the alias
|
4156
|
-
# {ColorRGB} is also available.
|
4157
|
-
class ColourRGB
|
4158
|
-
# @return [Integer] the red part of this colour (0-255).
|
4159
|
-
attr_reader :red
|
4160
|
-
|
4161
|
-
# @return [Integer] the green part of this colour (0-255).
|
4162
|
-
attr_reader :green
|
4163
|
-
|
4164
|
-
# @return [Integer] the blue part of this colour (0-255).
|
4165
|
-
attr_reader :blue
|
4166
|
-
|
4167
|
-
# @return [Integer] the colour's RGB values combined into one integer.
|
4168
|
-
attr_reader :combined
|
4169
|
-
alias_method :to_i, :combined
|
4170
|
-
|
4171
|
-
# Make a new colour from the combined value.
|
4172
|
-
# @param combined [Integer, String] The colour's RGB values combined into one integer or a hexadecimal string
|
4173
|
-
# @example Initialize a with a base 10 integer
|
4174
|
-
# ColourRGB.new(7506394) #=> ColourRGB
|
4175
|
-
# ColourRGB.new(0x7289da) #=> ColourRGB
|
4176
|
-
# @example Initialize a with a hexadecimal string
|
4177
|
-
# ColourRGB.new('7289da') #=> ColourRGB
|
4178
|
-
def initialize(combined)
|
4179
|
-
@combined = combined.is_a?(String) ? combined.to_i(16) : combined
|
4180
|
-
@red = (@combined >> 16) & 0xFF
|
4181
|
-
@green = (@combined >> 8) & 0xFF
|
4182
|
-
@blue = @combined & 0xFF
|
4183
|
-
end
|
4184
|
-
|
4185
|
-
# @return [String] the colour as a hexadecimal.
|
4186
|
-
def hex
|
4187
|
-
@combined.to_s(16)
|
4188
|
-
end
|
4189
|
-
alias_method :hexadecimal, :hex
|
4190
|
-
end
|
4191
|
-
|
4192
|
-
# Alias for the class {ColourRGB}
|
4193
|
-
ColorRGB = ColourRGB
|
4194
|
-
end
|
19
|
+
require 'discordrb/data/activity'
|
20
|
+
require 'discordrb/data/application'
|
21
|
+
require 'discordrb/data/user'
|
22
|
+
require 'discordrb/data/voice_state'
|
23
|
+
require 'discordrb/data/voice_region'
|
24
|
+
require 'discordrb/data/member'
|
25
|
+
require 'discordrb/data/recipient'
|
26
|
+
require 'discordrb/data/profile'
|
27
|
+
require 'discordrb/data/role'
|
28
|
+
require 'discordrb/data/invite'
|
29
|
+
require 'discordrb/data/overwrite'
|
30
|
+
require 'discordrb/data/channel'
|
31
|
+
require 'discordrb/data/embed'
|
32
|
+
require 'discordrb/data/attachment'
|
33
|
+
require 'discordrb/data/message'
|
34
|
+
require 'discordrb/data/reaction'
|
35
|
+
require 'discordrb/data/emoji'
|
36
|
+
require 'discordrb/data/integration'
|
37
|
+
require 'discordrb/data/server'
|
38
|
+
require 'discordrb/data/webhook'
|
39
|
+
require 'discordrb/data/audit_logs'
|