discordrb 3.3.0 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +152 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
- data/.github/pull_request_template.md +37 -0
- data/.github/workflows/codeql.yml +65 -0
- data/.markdownlint.json +4 -0
- data/.rubocop.yml +39 -36
- data/CHANGELOG.md +874 -552
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +80 -86
- data/Rakefile +2 -0
- data/bin/console +1 -0
- data/discordrb-webhooks.gemspec +9 -6
- data/discordrb.gemspec +21 -18
- data/lib/discordrb/allowed_mentions.rb +36 -0
- data/lib/discordrb/api/application.rb +202 -0
- data/lib/discordrb/api/channel.rb +236 -47
- data/lib/discordrb/api/interaction.rb +54 -0
- data/lib/discordrb/api/invite.rb +5 -5
- data/lib/discordrb/api/server.rb +94 -66
- data/lib/discordrb/api/user.rb +17 -11
- data/lib/discordrb/api/webhook.rb +63 -6
- data/lib/discordrb/api.rb +55 -16
- data/lib/discordrb/await.rb +0 -1
- data/lib/discordrb/bot.rb +480 -93
- data/lib/discordrb/cache.rb +31 -24
- data/lib/discordrb/colour_rgb.rb +43 -0
- data/lib/discordrb/commands/command_bot.rb +35 -12
- data/lib/discordrb/commands/container.rb +21 -24
- data/lib/discordrb/commands/parser.rb +20 -20
- data/lib/discordrb/commands/rate_limiter.rb +4 -3
- data/lib/discordrb/container.rb +209 -20
- data/lib/discordrb/data/activity.rb +271 -0
- data/lib/discordrb/data/application.rb +50 -0
- data/lib/discordrb/data/attachment.rb +71 -0
- data/lib/discordrb/data/audit_logs.rb +345 -0
- data/lib/discordrb/data/channel.rb +993 -0
- data/lib/discordrb/data/component.rb +229 -0
- data/lib/discordrb/data/embed.rb +251 -0
- data/lib/discordrb/data/emoji.rb +82 -0
- data/lib/discordrb/data/integration.rb +122 -0
- data/lib/discordrb/data/interaction.rb +800 -0
- data/lib/discordrb/data/invite.rb +137 -0
- data/lib/discordrb/data/member.rb +372 -0
- data/lib/discordrb/data/message.rb +414 -0
- data/lib/discordrb/data/overwrite.rb +108 -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 +248 -0
- data/lib/discordrb/data/server.rb +1004 -0
- data/lib/discordrb/data/user.rb +264 -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 +238 -0
- data/lib/discordrb/data.rb +28 -4180
- data/lib/discordrb/errors.rb +46 -4
- data/lib/discordrb/events/bans.rb +7 -5
- data/lib/discordrb/events/channels.rb +3 -1
- data/lib/discordrb/events/guilds.rb +16 -9
- data/lib/discordrb/events/interactions.rb +482 -0
- data/lib/discordrb/events/invites.rb +125 -0
- data/lib/discordrb/events/members.rb +6 -2
- data/lib/discordrb/events/message.rb +72 -27
- data/lib/discordrb/events/presence.rb +35 -18
- data/lib/discordrb/events/raw.rb +1 -3
- data/lib/discordrb/events/reactions.rb +49 -4
- data/lib/discordrb/events/threads.rb +96 -0
- 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 +99 -71
- 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 +159 -39
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +16 -7
- data/lib/discordrb/voice/network.rb +99 -47
- data/lib/discordrb/voice/sodium.rb +98 -0
- data/lib/discordrb/voice/voice_bot.rb +33 -25
- data/lib/discordrb/webhooks.rb +2 -0
- data/lib/discordrb.rb +107 -1
- metadata +126 -54
- data/.codeclimate.yml +0 -16
- data/.travis.yml +0 -33
- data/bin/travis_build_docs.sh +0 -17
- /data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +0 -0
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,31 @@ 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/webhooks/view'
|
16
|
+
require 'discordrb/paginator'
|
14
17
|
require 'time'
|
15
18
|
require 'base64'
|
16
19
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
20
|
+
require 'discordrb/data/activity'
|
21
|
+
require 'discordrb/data/application'
|
22
|
+
require 'discordrb/data/user'
|
23
|
+
require 'discordrb/data/voice_state'
|
24
|
+
require 'discordrb/data/voice_region'
|
25
|
+
require 'discordrb/data/member'
|
26
|
+
require 'discordrb/data/recipient'
|
27
|
+
require 'discordrb/data/profile'
|
28
|
+
require 'discordrb/data/role'
|
29
|
+
require 'discordrb/data/invite'
|
30
|
+
require 'discordrb/data/overwrite'
|
31
|
+
require 'discordrb/data/channel'
|
32
|
+
require 'discordrb/data/embed'
|
33
|
+
require 'discordrb/data/attachment'
|
34
|
+
require 'discordrb/data/message'
|
35
|
+
require 'discordrb/data/reaction'
|
36
|
+
require 'discordrb/data/emoji'
|
37
|
+
require 'discordrb/data/integration'
|
38
|
+
require 'discordrb/data/server'
|
39
|
+
require 'discordrb/data/webhook'
|
40
|
+
require 'discordrb/data/audit_logs'
|
41
|
+
require 'discordrb/data/interaction'
|
42
|
+
require 'discordrb/data/component'
|