discordrb 3.1.1 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of discordrb might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.circleci/config.yml +126 -0
- data/.codeclimate.yml +16 -0
- data/.github/CONTRIBUTING.md +13 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- data/.github/pull_request_template.md +37 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +39 -33
- data/.travis.yml +27 -2
- data/.yardopts +1 -1
- data/CHANGELOG.md +808 -208
- data/Gemfile +4 -1
- data/LICENSE.txt +1 -1
- data/README.md +108 -53
- data/Rakefile +14 -1
- data/bin/console +1 -0
- data/bin/travis_build_docs.sh +17 -0
- data/discordrb-webhooks.gemspec +26 -0
- data/discordrb.gemspec +24 -15
- data/lib/discordrb.rb +75 -2
- data/lib/discordrb/allowed_mentions.rb +36 -0
- data/lib/discordrb/api.rb +126 -27
- data/lib/discordrb/api/channel.rb +165 -43
- data/lib/discordrb/api/invite.rb +10 -7
- data/lib/discordrb/api/server.rb +240 -61
- data/lib/discordrb/api/user.rb +26 -24
- data/lib/discordrb/api/webhook.rb +83 -0
- data/lib/discordrb/await.rb +1 -2
- data/lib/discordrb/bot.rb +417 -149
- data/lib/discordrb/cache.rb +42 -10
- data/lib/discordrb/colour_rgb.rb +43 -0
- data/lib/discordrb/commands/command_bot.rb +186 -31
- data/lib/discordrb/commands/container.rb +30 -16
- data/lib/discordrb/commands/parser.rb +102 -47
- data/lib/discordrb/commands/rate_limiter.rb +18 -17
- data/lib/discordrb/container.rb +245 -41
- data/lib/discordrb/data.rb +27 -2511
- data/lib/discordrb/data/activity.rb +264 -0
- data/lib/discordrb/data/application.rb +50 -0
- data/lib/discordrb/data/attachment.rb +56 -0
- data/lib/discordrb/data/audit_logs.rb +345 -0
- data/lib/discordrb/data/channel.rb +849 -0
- data/lib/discordrb/data/embed.rb +251 -0
- data/lib/discordrb/data/emoji.rb +82 -0
- data/lib/discordrb/data/integration.rb +83 -0
- data/lib/discordrb/data/invite.rb +137 -0
- data/lib/discordrb/data/member.rb +297 -0
- data/lib/discordrb/data/message.rb +334 -0
- data/lib/discordrb/data/overwrite.rb +102 -0
- data/lib/discordrb/data/profile.rb +91 -0
- data/lib/discordrb/data/reaction.rb +33 -0
- data/lib/discordrb/data/recipient.rb +34 -0
- data/lib/discordrb/data/role.rb +191 -0
- data/lib/discordrb/data/server.rb +1002 -0
- data/lib/discordrb/data/user.rb +204 -0
- data/lib/discordrb/data/voice_region.rb +45 -0
- data/lib/discordrb/data/voice_state.rb +41 -0
- data/lib/discordrb/data/webhook.rb +145 -0
- data/lib/discordrb/errors.rb +36 -2
- data/lib/discordrb/events/bans.rb +7 -5
- data/lib/discordrb/events/channels.rb +2 -0
- data/lib/discordrb/events/generic.rb +19 -3
- data/lib/discordrb/events/guilds.rb +129 -6
- data/lib/discordrb/events/invites.rb +125 -0
- data/lib/discordrb/events/members.rb +6 -2
- data/lib/discordrb/events/message.rb +86 -36
- data/lib/discordrb/events/presence.rb +23 -16
- data/lib/discordrb/events/raw.rb +47 -0
- data/lib/discordrb/events/reactions.rb +159 -0
- data/lib/discordrb/events/roles.rb +7 -6
- data/lib/discordrb/events/typing.rb +9 -5
- data/lib/discordrb/events/voice_server_update.rb +47 -0
- data/lib/discordrb/events/voice_state_update.rb +29 -9
- data/lib/discordrb/events/webhooks.rb +64 -0
- data/lib/discordrb/gateway.rb +219 -88
- data/lib/discordrb/id_object.rb +39 -0
- data/lib/discordrb/light.rb +1 -1
- data/lib/discordrb/light/integrations.rb +1 -1
- data/lib/discordrb/light/light_bot.rb +1 -1
- data/lib/discordrb/logger.rb +12 -11
- data/lib/discordrb/paginator.rb +57 -0
- data/lib/discordrb/permissions.rb +148 -14
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +14 -15
- data/lib/discordrb/voice/network.rb +86 -45
- data/lib/discordrb/voice/sodium.rb +96 -0
- data/lib/discordrb/voice/voice_bot.rb +52 -40
- data/lib/discordrb/webhooks.rb +12 -0
- data/lib/discordrb/websocket.rb +2 -2
- metadata +137 -34
data/lib/discordrb/data.rb
CHANGED
@@ -1,2523 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
require 'ostruct'
|
3
|
+
require 'discordrb/allowed_mentions'
|
6
4
|
require 'discordrb/permissions'
|
5
|
+
require 'discordrb/id_object'
|
6
|
+
require 'discordrb/colour_rgb'
|
7
7
|
require 'discordrb/errors'
|
8
8
|
require 'discordrb/api'
|
9
9
|
require 'discordrb/api/channel'
|
10
10
|
require 'discordrb/api/server'
|
11
11
|
require 'discordrb/api/invite'
|
12
12
|
require 'discordrb/api/user'
|
13
|
+
require 'discordrb/api/webhook'
|
14
|
+
require 'discordrb/webhooks/embeds'
|
15
|
+
require 'discordrb/paginator'
|
13
16
|
require 'time'
|
14
17
|
require 'base64'
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
lines = msg.lines
|
38
|
-
|
39
|
-
# Turn the message into a "triangle" of consecutively longer slices, for example the array [1,2,3,4] would become
|
40
|
-
# [
|
41
|
-
# [1],
|
42
|
-
# [1, 2],
|
43
|
-
# [1, 2, 3],
|
44
|
-
# [1, 2, 3, 4]
|
45
|
-
# ]
|
46
|
-
tri = [*0..(lines.length - 1)].map { |i| lines.combination(i + 1).first }
|
47
|
-
|
48
|
-
# Join the individual elements together to get an array of strings with consecutively more lines
|
49
|
-
joined = tri.map(&:join)
|
50
|
-
|
51
|
-
# Find the largest element that is still below the character limit, or if none such element exists return the first
|
52
|
-
ideal = joined.max_by { |e| e.length > CHARACTER_LIMIT ? -1 : e.length }
|
53
|
-
|
54
|
-
# If it's still larger than the character limit (none was smaller than it) split it into slices with the length
|
55
|
-
# being the character limit, otherwise just return an array with one element
|
56
|
-
ideal_ary = ideal.length > CHARACTER_LIMIT ? ideal.chars.each_slice(CHARACTER_LIMIT).map(&:join) : [ideal]
|
57
|
-
|
58
|
-
# Slice off the ideal part and strip newlines
|
59
|
-
rest = msg[ideal.length..-1].strip
|
60
|
-
|
61
|
-
# If none remains, return an empty array -> we're done
|
62
|
-
return [] unless rest
|
63
|
-
|
64
|
-
# Otherwise, call the method recursively to split the rest of the string and add it onto the ideal array
|
65
|
-
ideal_ary + split_message(rest)
|
66
|
-
end
|
67
|
-
|
68
|
-
# Mixin for objects that have IDs
|
69
|
-
module IDObject
|
70
|
-
# @return [Integer] the ID which uniquely identifies this object across Discord.
|
71
|
-
attr_reader :id
|
72
|
-
alias_method :resolve_id, :id
|
73
|
-
|
74
|
-
# ID based comparison
|
75
|
-
def ==(other)
|
76
|
-
Discordrb.id_compare(@id, other)
|
77
|
-
end
|
78
|
-
|
79
|
-
# Estimates the time this object was generated on based on the beginning of the ID. This is fairly accurate but
|
80
|
-
# shouldn't be relied on as Discord might change its algorithm at any time
|
81
|
-
# @return [Time] when this object was created at
|
82
|
-
def creation_time
|
83
|
-
# Milliseconds
|
84
|
-
ms = (@id >> 22) + DISCORD_EPOCH
|
85
|
-
Time.at(ms / 1000.0)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
# Mixin for the attributes users should have
|
90
|
-
module UserAttributes
|
91
|
-
# @return [String] this user's username
|
92
|
-
attr_reader :username
|
93
|
-
alias_method :name, :username
|
94
|
-
|
95
|
-
# @return [String] this user's discriminator which is used internally to identify users with identical usernames.
|
96
|
-
attr_reader :discriminator
|
97
|
-
alias_method :discrim, :discriminator
|
98
|
-
alias_method :tag, :discriminator
|
99
|
-
alias_method :discord_tag, :discriminator
|
100
|
-
|
101
|
-
# @return [true, false] whether this user is a Discord bot account
|
102
|
-
attr_reader :bot_account
|
103
|
-
alias_method :bot_account?, :bot_account
|
104
|
-
|
105
|
-
# @return [String] the ID of this user's current avatar, can be used to generate an avatar URL.
|
106
|
-
# @see #avatar_url
|
107
|
-
attr_reader :avatar_id
|
108
|
-
|
109
|
-
# Utility function to mention users in messages
|
110
|
-
# @return [String] the mention code in the form of <@id>
|
111
|
-
def mention
|
112
|
-
"<@#{@id}>"
|
113
|
-
end
|
114
|
-
|
115
|
-
# Utility function to get Discord's distinct representation of a user, i. e. username + discriminator
|
116
|
-
# @return [String] distinct representation of user
|
117
|
-
def distinct
|
118
|
-
"#{@username}##{@discriminator}"
|
119
|
-
end
|
120
|
-
|
121
|
-
# Utility function to get a user's avatar URL.
|
122
|
-
# @return [String] the URL to the avatar image.
|
123
|
-
def avatar_url
|
124
|
-
API::User.avatar_url(@id, @avatar_id)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
# User on Discord, including internal data like discriminators
|
129
|
-
class User
|
130
|
-
include IDObject
|
131
|
-
include UserAttributes
|
132
|
-
|
133
|
-
# @!attribute [r] status
|
134
|
-
# @return [Symbol] the current online status of the user (`:online`, `:offline` or `:idle`)
|
135
|
-
attr_accessor :status
|
136
|
-
|
137
|
-
# @!attribute [r] game
|
138
|
-
# @return [String, nil] the game the user is currently playing, or `nil` if none is being played.
|
139
|
-
attr_accessor :game
|
140
|
-
|
141
|
-
def initialize(data, bot)
|
142
|
-
@bot = bot
|
143
|
-
|
144
|
-
@username = data['username']
|
145
|
-
@id = data['id'].to_i
|
146
|
-
@discriminator = data['discriminator']
|
147
|
-
@avatar_id = data['avatar']
|
148
|
-
@roles = {}
|
149
|
-
|
150
|
-
@bot_account = false
|
151
|
-
@bot_account = true if data['bot']
|
152
|
-
|
153
|
-
@status = :offline
|
154
|
-
end
|
155
|
-
|
156
|
-
# Get a user's PM channel or send them a PM
|
157
|
-
# @overload pm
|
158
|
-
# Creates a private message channel for this user or returns an existing one if it already exists
|
159
|
-
# @return [Channel] the PM channel to this user.
|
160
|
-
# @overload pm(content)
|
161
|
-
# Sends a private to this user.
|
162
|
-
# @param content [String] The content to send.
|
163
|
-
# @return [Message] the message sent to this user.
|
164
|
-
def pm(content = nil)
|
165
|
-
if content
|
166
|
-
# Recursively call pm to get the channel, then send a message to it
|
167
|
-
channel = pm
|
168
|
-
channel.send_message(content)
|
169
|
-
else
|
170
|
-
# If no message was specified, return the PM channel
|
171
|
-
@bot.pm_channel(@id)
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
alias_method :dm, :pm
|
176
|
-
|
177
|
-
# Send the user a file.
|
178
|
-
# @param file [File] The file to send to the user
|
179
|
-
# @param caption [String] The caption of the file being sent
|
180
|
-
# @return [Message] the message sent to this user.
|
181
|
-
def send_file(file, caption = nil)
|
182
|
-
pm.send_file(file, caption: caption)
|
183
|
-
end
|
184
|
-
|
185
|
-
# Set the user's name
|
186
|
-
# @note for internal use only
|
187
|
-
# @!visibility private
|
188
|
-
def update_username(username)
|
189
|
-
@username = username
|
190
|
-
end
|
191
|
-
|
192
|
-
# Add an await for a message from this user. Specifically, this adds a global await for a MessageEvent with this
|
193
|
-
# user's ID as a :from attribute.
|
194
|
-
# @see Bot#add_await
|
195
|
-
def await(key, attributes = {}, &block)
|
196
|
-
@bot.add_await(key, Discordrb::Events::MessageEvent, { from: @id }.merge(attributes), &block)
|
197
|
-
end
|
198
|
-
|
199
|
-
# Gets the member this user is on a server
|
200
|
-
# @param server [Server] The server to get the member for
|
201
|
-
# @return [Member] this user as a member on a particular server
|
202
|
-
def on(server)
|
203
|
-
id = server.resolve_id
|
204
|
-
@bot.server(id).member(@id)
|
205
|
-
end
|
206
|
-
|
207
|
-
# Is the user the bot?
|
208
|
-
# @return [true, false] whether this user is the bot
|
209
|
-
def current_bot?
|
210
|
-
@bot.profile.id == @id
|
211
|
-
end
|
212
|
-
|
213
|
-
# @return [true, false] whether this user is a fake user for a webhook message
|
214
|
-
def webhook?
|
215
|
-
@discriminator == Message::ZERO_DISCRIM
|
216
|
-
end
|
217
|
-
|
218
|
-
[:offline, :idle, :online].each do |e|
|
219
|
-
define_method(e.to_s + '?') do
|
220
|
-
@status.to_sym == e
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
# The inspect method is overwritten to give more useful output
|
225
|
-
def inspect
|
226
|
-
"<User username=#{@username} id=#{@id} discriminator=#{@discriminator}>"
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
# OAuth Application information
|
231
|
-
class Application
|
232
|
-
include IDObject
|
233
|
-
|
234
|
-
# @return [String] the application name
|
235
|
-
attr_reader :name
|
236
|
-
|
237
|
-
# @return [String] the application description
|
238
|
-
attr_reader :description
|
239
|
-
|
240
|
-
# @return [Array<String>] the applications origins permitted to use RPC
|
241
|
-
attr_reader :rpc_origins
|
242
|
-
|
243
|
-
# @return [Integer]
|
244
|
-
attr_reader :flags
|
245
|
-
|
246
|
-
# Gets the user object of the owner. May be limited to username, discriminator,
|
247
|
-
# ID and avatar if the bot cannot reach the owner.
|
248
|
-
# @return [User] the user object of the owner
|
249
|
-
attr_reader :owner
|
250
|
-
|
251
|
-
def initialize(data, bot)
|
252
|
-
@bot = bot
|
253
|
-
|
254
|
-
@name = data['name']
|
255
|
-
@id = data['id'].to_i
|
256
|
-
@description = data['description']
|
257
|
-
@icon_id = data['icon']
|
258
|
-
@rpc_origins = data['rpc_origins']
|
259
|
-
@flags = data['flags']
|
260
|
-
@owner = @bot.ensure_user(data['owner'])
|
261
|
-
end
|
262
|
-
|
263
|
-
# Utility function to get a application's icon URL.
|
264
|
-
# @return [String, nil] the URL to the icon image (nil if no image is set).
|
265
|
-
def icon_url
|
266
|
-
return nil if @icon_id.nil?
|
267
|
-
API.app_icon_url(@id, @icon_id)
|
268
|
-
end
|
269
|
-
|
270
|
-
# The inspect method is overwritten to give more useful output
|
271
|
-
def inspect
|
272
|
-
"<Application name=#{@name} id=#{@id}>"
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
# Mixin for the attributes members and private members should have
|
277
|
-
module MemberAttributes
|
278
|
-
# @return [Time] when this member joined the server.
|
279
|
-
attr_reader :joined_at
|
280
|
-
|
281
|
-
# @return [String, nil] the nickname this member has, or nil if it has none.
|
282
|
-
attr_reader :nick
|
283
|
-
alias_method :nickname, :nick
|
284
|
-
|
285
|
-
# @return [Array<Role>] the roles this member has.
|
286
|
-
attr_reader :roles
|
287
|
-
|
288
|
-
# @return [Server] the server this member is on.
|
289
|
-
attr_reader :server
|
290
|
-
end
|
291
|
-
|
292
|
-
# Mixin to calculate resulting permissions from overrides etc.
|
293
|
-
module PermissionCalculator
|
294
|
-
# Checks whether this user can do the particular action, regardless of whether it has the permission defined,
|
295
|
-
# through for example being the server owner or having the Manage Roles permission
|
296
|
-
# @param action [Symbol] The permission that should be checked. See also {Permissions::Flags} for a list.
|
297
|
-
# @param channel [Channel, nil] If channel overrides should be checked too, this channel specifies where the overrides should be checked.
|
298
|
-
# @example Check if the bot can send messages to a specific channel in a server.
|
299
|
-
# bot_profile = bot.profile.on(event.server)
|
300
|
-
# can_send_messages = bot_profile.permission?(:send_messages, channel)
|
301
|
-
# @return [true, false] whether or not this user has the permission.
|
302
|
-
def permission?(action, channel = nil)
|
303
|
-
# If the member is the server owner, it irrevocably has all permissions.
|
304
|
-
return true if owner?
|
305
|
-
|
306
|
-
# First, check whether the user has Manage Roles defined.
|
307
|
-
# (Coincidentally, Manage Permissions is the same permission as Manage Roles, and a
|
308
|
-
# Manage Permissions deny overwrite will override Manage Roles, so we can just check for
|
309
|
-
# Manage Roles once and call it a day.)
|
310
|
-
return true if defined_permission?(:administrator, channel)
|
311
|
-
|
312
|
-
# Otherwise, defer to defined_permission
|
313
|
-
defined_permission?(action, channel)
|
314
|
-
end
|
315
|
-
|
316
|
-
# Checks whether this user has a particular permission defined (i. e. not implicit, through for example
|
317
|
-
# Manage Roles)
|
318
|
-
# @param action [Symbol] The permission that should be checked. See also {Permissions::Flags} for a list.
|
319
|
-
# @param channel [Channel, nil] If channel overrides should be checked too, this channel specifies where the overrides should be checked.
|
320
|
-
# @example Check if a member has the Manage Channels permission defined in the server.
|
321
|
-
# has_manage_channels = member.defined_permission?(:manage_channels)
|
322
|
-
# @return [true, false] whether or not this user has the permission defined.
|
323
|
-
def defined_permission?(action, channel = nil)
|
324
|
-
# Get the permission the user's roles have
|
325
|
-
role_permission = defined_role_permission?(action, channel)
|
326
|
-
|
327
|
-
# Once we have checked the role permission, we have to check the channel overrides for the
|
328
|
-
# specific user
|
329
|
-
user_specific_override = permission_overwrite(action, channel, id) # Use the ID reader as members have no ID instance variable
|
330
|
-
|
331
|
-
# Merge the two permissions - if an override is defined, it has to be allow, otherwise we only care about the role
|
332
|
-
return role_permission unless user_specific_override
|
333
|
-
user_specific_override == :allow
|
334
|
-
end
|
335
|
-
|
336
|
-
# Define methods for querying permissions
|
337
|
-
Discordrb::Permissions::Flags.each_value do |flag|
|
338
|
-
define_method "can_#{flag}?" do |channel = nil|
|
339
|
-
permission? flag, channel
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
alias_method :can_administrate?, :can_administrator?
|
344
|
-
|
345
|
-
private
|
346
|
-
|
347
|
-
def defined_role_permission?(action, channel)
|
348
|
-
# For each role, check if
|
349
|
-
# (1) the channel explicitly allows or permits an action for the role and
|
350
|
-
# (2) if the user is allowed to do the action if the channel doesn't specify
|
351
|
-
@roles.reduce(false) do |can_act, role|
|
352
|
-
# Get the override defined for the role on the channel
|
353
|
-
channel_allow = permission_overwrite(action, channel, role.id)
|
354
|
-
can_act = if channel_allow
|
355
|
-
# If the channel has an override, check whether it is an allow - if yes,
|
356
|
-
# the user can act, if not, it can't
|
357
|
-
channel_allow == :allow
|
358
|
-
else
|
359
|
-
# Otherwise defer to the role
|
360
|
-
role.permissions.instance_variable_get("@#{action}") || can_act
|
361
|
-
end
|
362
|
-
can_act
|
363
|
-
end
|
364
|
-
end
|
365
|
-
|
366
|
-
def permission_overwrite(action, channel, id)
|
367
|
-
# If no overwrites are defined, or no channel is set, no overwrite will be present
|
368
|
-
return nil unless channel && channel.permission_overwrites[id]
|
369
|
-
|
370
|
-
# Otherwise, check the allow and deny objects
|
371
|
-
allow = channel.permission_overwrites[id].allow
|
372
|
-
deny = channel.permission_overwrites[id].deny
|
373
|
-
if allow.instance_variable_get("@#{action}")
|
374
|
-
:allow
|
375
|
-
elsif deny.instance_variable_get("@#{action}")
|
376
|
-
:deny
|
377
|
-
end
|
378
|
-
|
379
|
-
# If there's no variable defined, nil will implicitly be returned
|
380
|
-
end
|
381
|
-
end
|
382
|
-
|
383
|
-
# A voice state represents the state of a member's connection to a voice channel. It includes data like the voice
|
384
|
-
# channel the member is connected to and mute/deaf flags.
|
385
|
-
class VoiceState
|
386
|
-
# @return [Integer] the ID of the user whose voice state is represented by this object.
|
387
|
-
attr_reader :user_id
|
388
|
-
|
389
|
-
# @return [true, false] whether this voice state's member is muted server-wide.
|
390
|
-
attr_reader :mute
|
391
|
-
|
392
|
-
# @return [true, false] whether this voice state's member is deafened server-wide.
|
393
|
-
attr_reader :deaf
|
394
|
-
|
395
|
-
# @return [true, false] whether this voice state's member has muted themselves.
|
396
|
-
attr_reader :self_mute
|
397
|
-
|
398
|
-
# @return [true, false] whether this voice state's member has deafened themselves.
|
399
|
-
attr_reader :self_deaf
|
400
|
-
|
401
|
-
# @return [Channel] the voice channel this voice state's member is in.
|
402
|
-
attr_reader :voice_channel
|
403
|
-
|
404
|
-
# @!visibility private
|
405
|
-
def initialize(user_id)
|
406
|
-
@user_id = user_id
|
407
|
-
end
|
408
|
-
|
409
|
-
# Update this voice state with new data from Discord
|
410
|
-
# @note For internal use only.
|
411
|
-
# @!visibility private
|
412
|
-
def update(channel, mute, deaf, self_mute, self_deaf)
|
413
|
-
@voice_channel = channel
|
414
|
-
@mute = mute
|
415
|
-
@deaf = deaf
|
416
|
-
@self_mute = self_mute
|
417
|
-
@self_deaf = self_deaf
|
418
|
-
end
|
419
|
-
end
|
420
|
-
|
421
|
-
# A member is a user on a server. It differs from regular users in that it has roles, voice statuses and things like
|
422
|
-
# that.
|
423
|
-
class Member < DelegateClass(User)
|
424
|
-
# @return [true, false] whether this member is muted server-wide.
|
425
|
-
def mute
|
426
|
-
voice_state_attribute(:mute)
|
427
|
-
end
|
428
|
-
|
429
|
-
# @return [true, false] whether this member is deafened server-wide.
|
430
|
-
def deaf
|
431
|
-
voice_state_attribute(:deaf)
|
432
|
-
end
|
433
|
-
|
434
|
-
# @return [true, false] whether this member has muted themselves.
|
435
|
-
def self_mute
|
436
|
-
voice_state_attribute(:self_mute)
|
437
|
-
end
|
438
|
-
|
439
|
-
# @return [true, false] whether this member has deafened themselves.
|
440
|
-
def self_deaf
|
441
|
-
voice_state_attribute(:self_deaf)
|
442
|
-
end
|
443
|
-
|
444
|
-
# @return [Channel] the voice channel this member is in.
|
445
|
-
def voice_channel
|
446
|
-
voice_state_attribute(:voice_channel)
|
447
|
-
end
|
448
|
-
|
449
|
-
alias_method :muted?, :mute
|
450
|
-
alias_method :deafened?, :deaf
|
451
|
-
alias_method :self_muted?, :self_mute
|
452
|
-
alias_method :self_deafened?, :self_deaf
|
453
|
-
|
454
|
-
include MemberAttributes
|
455
|
-
|
456
|
-
# @!visibility private
|
457
|
-
def initialize(data, server, bot)
|
458
|
-
@bot = bot
|
459
|
-
|
460
|
-
@user = bot.ensure_user(data['user'])
|
461
|
-
super @user # Initialize the delegate class
|
462
|
-
|
463
|
-
# Somehow, Discord doesn't send the server ID in the standard member format...
|
464
|
-
raise ArgumentError, 'Cannot create a member without any information about the server!' if server.nil? && data['guild_id'].nil?
|
465
|
-
@server = server || bot.server(data['guild_id'].to_i)
|
466
|
-
|
467
|
-
# Initialize the roles by getting the roles from the server one-by-one
|
468
|
-
update_roles(data['roles'])
|
469
|
-
|
470
|
-
@nick = data['nick']
|
471
|
-
@joined_at = data['joined_at'] ? Time.parse(data['joined_at']) : nil
|
472
|
-
end
|
473
|
-
|
474
|
-
# @return [true, false] whether this member is the server owner.
|
475
|
-
def owner?
|
476
|
-
@server.owner == self
|
477
|
-
end
|
478
|
-
|
479
|
-
# @param role [Role, Integer, #resolve_id] the role to check or its ID.
|
480
|
-
# @return [true, false] whether this member has the specified role.
|
481
|
-
def role?(role)
|
482
|
-
role = role.resolve_id
|
483
|
-
@roles.any? { |e| e.id == role }
|
484
|
-
end
|
485
|
-
|
486
|
-
# Bulk sets a member's roles.
|
487
|
-
# @param role [Role, Array<Role>] The role(s) to set.
|
488
|
-
def roles=(role)
|
489
|
-
role_ids = role_id_array(role)
|
490
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, roles: role_ids)
|
491
|
-
end
|
492
|
-
|
493
|
-
# Adds and removes roles from a member.
|
494
|
-
# @param add [Role, Array<Role>] The role(s) to add.
|
495
|
-
# @param remove [Role, Array<Role>] The role(s) to remove.
|
496
|
-
# @example Remove the 'Member' role from a user, and add the 'Muted' role to them.
|
497
|
-
# to_add = server.roles.find {|role| role.name == 'Muted'}
|
498
|
-
# to_remove = server.roles.find {|role| role.name == 'Member'}
|
499
|
-
# member.modify_roles(to_add, to_remove)
|
500
|
-
def modify_roles(add, remove)
|
501
|
-
add_role_ids = role_id_array(add)
|
502
|
-
remove_role_ids = role_id_array(remove)
|
503
|
-
old_role_ids = @roles.map(&:id)
|
504
|
-
new_role_ids = (old_role_ids - remove_role_ids + add_role_ids).uniq
|
505
|
-
|
506
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, roles: new_role_ids)
|
507
|
-
end
|
508
|
-
|
509
|
-
# Adds one or more roles to this member.
|
510
|
-
# @param role [Role, Array<Role>] The role(s) to add.
|
511
|
-
def add_role(role)
|
512
|
-
role_ids = role_id_array(role)
|
513
|
-
old_role_ids = @roles.map(&:id)
|
514
|
-
new_role_ids = (old_role_ids + role_ids).uniq
|
515
|
-
|
516
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, roles: new_role_ids)
|
517
|
-
end
|
518
|
-
|
519
|
-
# Removes one or more roles from this member.
|
520
|
-
# @param role [Role, Array<Role>] The role(s) to remove.
|
521
|
-
def remove_role(role)
|
522
|
-
old_role_ids = @roles.map(&:id)
|
523
|
-
role_ids = role_id_array(role)
|
524
|
-
new_role_ids = old_role_ids.reject { |i| role_ids.include?(i) }
|
525
|
-
|
526
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, roles: new_role_ids)
|
527
|
-
end
|
528
|
-
|
529
|
-
# Server deafens this member.
|
530
|
-
def server_deafen
|
531
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, deaf: true)
|
532
|
-
end
|
533
|
-
|
534
|
-
# Server undeafens this member.
|
535
|
-
def server_undeafen
|
536
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, deaf: false)
|
537
|
-
end
|
538
|
-
|
539
|
-
# Server mutes this member.
|
540
|
-
def server_mute
|
541
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, mute: true)
|
542
|
-
end
|
543
|
-
|
544
|
-
# Server unmutes this member.
|
545
|
-
def server_unmute
|
546
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, mute: false)
|
547
|
-
end
|
548
|
-
|
549
|
-
# Sets or resets this member's nickname. Requires the Change Nickname permission for the bot itself and Manage
|
550
|
-
# Nicknames for other users.
|
551
|
-
# @param nick [String, nil] The string to set the nickname to, or nil if it should be reset.
|
552
|
-
def nick=(nick)
|
553
|
-
# Discord uses the empty string to signify 'no nickname' so we convert nil into that
|
554
|
-
nick ||= ''
|
555
|
-
|
556
|
-
if @user.current_bot?
|
557
|
-
API::User.change_own_nickname(@bot.token, @server.id, nick)
|
558
|
-
else
|
559
|
-
API::Server.update_member(@bot.token, @server.id, @user.id, nick: nick)
|
560
|
-
end
|
561
|
-
end
|
562
|
-
|
563
|
-
alias_method :nickname=, :nick=
|
564
|
-
|
565
|
-
# @return [String] the name the user displays as (nickname if they have one, username otherwise)
|
566
|
-
def display_name
|
567
|
-
nickname || username
|
568
|
-
end
|
569
|
-
|
570
|
-
# Update this member's roles
|
571
|
-
# @note For internal use only.
|
572
|
-
# @!visibility private
|
573
|
-
def update_roles(roles)
|
574
|
-
@roles = roles.map do |role|
|
575
|
-
role.is_a?(Role) ? role : @server.role(role.to_i)
|
576
|
-
end
|
577
|
-
end
|
578
|
-
|
579
|
-
# Update this member's nick
|
580
|
-
# @note For internal use only.
|
581
|
-
# @!visibility private
|
582
|
-
def update_nick(nick)
|
583
|
-
@nick = nick
|
584
|
-
end
|
585
|
-
|
586
|
-
include PermissionCalculator
|
587
|
-
|
588
|
-
# Overwriting inspect for debug purposes
|
589
|
-
def inspect
|
590
|
-
"<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}>"
|
591
|
-
end
|
592
|
-
|
593
|
-
private
|
594
|
-
|
595
|
-
# Utility method to get a list of role IDs from one role or an array of roles
|
596
|
-
def role_id_array(role)
|
597
|
-
if role.is_a? Array
|
598
|
-
role.map(&:resolve_id)
|
599
|
-
else
|
600
|
-
[role.resolve_id]
|
601
|
-
end
|
602
|
-
end
|
603
|
-
|
604
|
-
# Utility method to get data out of this member's voice state
|
605
|
-
def voice_state_attribute(name)
|
606
|
-
voice_state = @server.voice_states[@user.id]
|
607
|
-
voice_state.send name if voice_state
|
608
|
-
end
|
609
|
-
end
|
610
|
-
|
611
|
-
# Recipients are members on private channels - they exist for completeness purposes, but all
|
612
|
-
# the attributes will be empty.
|
613
|
-
class Recipient < DelegateClass(User)
|
614
|
-
include MemberAttributes
|
615
|
-
|
616
|
-
# @return [Channel] the private channel this recipient is the recipient of.
|
617
|
-
attr_reader :channel
|
618
|
-
|
619
|
-
# @!visibility private
|
620
|
-
def initialize(user, channel, bot)
|
621
|
-
@bot = bot
|
622
|
-
@channel = channel
|
623
|
-
raise ArgumentError, 'Tried to create a recipient for a public channel!' unless @channel.private?
|
624
|
-
|
625
|
-
@user = user
|
626
|
-
super @user
|
627
|
-
|
628
|
-
# Member attributes
|
629
|
-
@mute = @deaf = @self_mute = @self_deaf = false
|
630
|
-
@voice_channel = nil
|
631
|
-
@server = nil
|
632
|
-
@roles = []
|
633
|
-
@joined_at = @channel.creation_time
|
634
|
-
end
|
635
|
-
|
636
|
-
# Overwriting inspect for debug purposes
|
637
|
-
def inspect
|
638
|
-
"<Recipient user=#{@user.inspect} channel=#{@channel.inspect}>"
|
639
|
-
end
|
640
|
-
end
|
641
|
-
|
642
|
-
# This class is a special variant of User that represents the bot's user profile (things like own username and the avatar).
|
643
|
-
# It can be accessed using {Bot#profile}.
|
644
|
-
class Profile < User
|
645
|
-
def initialize(data, bot)
|
646
|
-
super(data, bot)
|
647
|
-
end
|
648
|
-
|
649
|
-
# Whether or not the user is the bot. The Profile can only ever be the bot user, so this always returns true.
|
650
|
-
# @return [true]
|
651
|
-
def current_bot?
|
652
|
-
true
|
653
|
-
end
|
654
|
-
|
655
|
-
# Sets the bot's username.
|
656
|
-
# @param username [String] The new username.
|
657
|
-
def username=(username)
|
658
|
-
update_profile_data(username: username)
|
659
|
-
end
|
660
|
-
|
661
|
-
alias_method :name=, :username=
|
662
|
-
|
663
|
-
# Changes the bot's avatar.
|
664
|
-
# @param avatar [String, #read] A JPG file to be used as the avatar, either
|
665
|
-
# something readable (e. g. File Object) or as a data URL.
|
666
|
-
def avatar=(avatar)
|
667
|
-
if avatar.respond_to? :read
|
668
|
-
# Set the file to binary mode if supported, so we don't get problems with Windows
|
669
|
-
avatar.binmode if avatar.respond_to?(:binmode)
|
670
|
-
|
671
|
-
avatar_string = 'data:image/jpg;base64,'
|
672
|
-
avatar_string += Base64.strict_encode64(avatar.read)
|
673
|
-
update_profile_data(avatar: avatar_string)
|
674
|
-
else
|
675
|
-
update_profile_data(avatar: avatar)
|
676
|
-
end
|
677
|
-
end
|
678
|
-
|
679
|
-
# Updates the cached profile data with the new one.
|
680
|
-
# @note For internal use only.
|
681
|
-
# @!visibility private
|
682
|
-
def update_data(new_data)
|
683
|
-
@username = new_data[:username] || @username
|
684
|
-
@avatar_id = new_data[:avatar_id] || @avatar_id
|
685
|
-
end
|
686
|
-
|
687
|
-
# Sets the user status setting to Online.
|
688
|
-
# @note Only usable on User accounts.
|
689
|
-
def online
|
690
|
-
update_profile_status_setting('online')
|
691
|
-
end
|
692
|
-
|
693
|
-
# Sets the user status setting to Idle.
|
694
|
-
# @note Only usable on User accounts.
|
695
|
-
def idle
|
696
|
-
update_profile_status_setting('idle')
|
697
|
-
end
|
698
|
-
|
699
|
-
# Sets the user status setting to Do Not Disturb.
|
700
|
-
# @note Only usable on User accounts.
|
701
|
-
def dnd
|
702
|
-
update_profile_status_setting('dnd')
|
703
|
-
end
|
704
|
-
|
705
|
-
alias_method(:busy, :dnd)
|
706
|
-
|
707
|
-
# Sets the user status setting to Invisible.
|
708
|
-
# @note Only usable on User accounts.
|
709
|
-
def invisible
|
710
|
-
update_profile_status_setting('invisible')
|
711
|
-
end
|
712
|
-
|
713
|
-
# The inspect method is overwritten to give more useful output
|
714
|
-
def inspect
|
715
|
-
"<Profile user=#{super}>"
|
716
|
-
end
|
717
|
-
|
718
|
-
private
|
719
|
-
|
720
|
-
# Internal handler for updating the user's status setting
|
721
|
-
def update_profile_status_setting(status)
|
722
|
-
API::User.change_status_setting(@bot.token, status)
|
723
|
-
end
|
724
|
-
|
725
|
-
def update_profile_data(new_data)
|
726
|
-
API::User.update_profile(@bot.token,
|
727
|
-
nil, nil,
|
728
|
-
new_data[:username] || @username,
|
729
|
-
new_data.key?(:avatar) ? new_data[:avatar] : @avatar_id)
|
730
|
-
update_data(new_data)
|
731
|
-
end
|
732
|
-
end
|
733
|
-
|
734
|
-
# A Discord role that contains permissions and applies to certain users
|
735
|
-
class Role
|
736
|
-
include IDObject
|
737
|
-
|
738
|
-
# @return [Permissions] this role's permissions.
|
739
|
-
attr_reader :permissions
|
740
|
-
|
741
|
-
# @return [String] this role's name ("new role" if it hasn't been changed)
|
742
|
-
attr_reader :name
|
743
|
-
|
744
|
-
# @return [true, false] whether or not this role should be displayed separately from other users
|
745
|
-
attr_reader :hoist
|
746
|
-
|
747
|
-
# @return [true, false] whether this role can be mentioned using a role mention
|
748
|
-
attr_reader :mentionable
|
749
|
-
alias_method :mentionable?, :mentionable
|
750
|
-
|
751
|
-
# @return [ColourRGB] the role colour
|
752
|
-
attr_reader :colour
|
753
|
-
alias_method :color, :colour
|
754
|
-
|
755
|
-
# @return [Integer] the position of this role in the hierarchy
|
756
|
-
attr_reader :position
|
757
|
-
|
758
|
-
# This class is used internally as a wrapper to a Role object that allows easy writing of permission data.
|
759
|
-
class RoleWriter
|
760
|
-
# @!visibility private
|
761
|
-
def initialize(role, token)
|
762
|
-
@role = role
|
763
|
-
@token = token
|
764
|
-
end
|
765
|
-
|
766
|
-
# Write the specified permission data to the role, without updating the permission cache
|
767
|
-
# @param bits [Integer] The packed permissions to write.
|
768
|
-
def write(bits)
|
769
|
-
@role.send(:packed=, bits, false)
|
770
|
-
end
|
771
|
-
|
772
|
-
# The inspect method is overridden, in this case to prevent the token being leaked
|
773
|
-
def inspect
|
774
|
-
"<RoleWriter role=#{@role} token=...>"
|
775
|
-
end
|
776
|
-
end
|
777
|
-
|
778
|
-
# @!visibility private
|
779
|
-
def initialize(data, bot, server = nil)
|
780
|
-
@bot = bot
|
781
|
-
@server = server
|
782
|
-
@permissions = Permissions.new(data['permissions'], RoleWriter.new(self, @bot.token))
|
783
|
-
@name = data['name']
|
784
|
-
@id = data['id'].to_i
|
785
|
-
|
786
|
-
@position = data['position']
|
787
|
-
|
788
|
-
@hoist = data['hoist']
|
789
|
-
@mentionable = data['mentionable']
|
790
|
-
|
791
|
-
@colour = ColourRGB.new(data['color'])
|
792
|
-
end
|
793
|
-
|
794
|
-
# @return [String] a string that will mention this role, if it is mentionable.
|
795
|
-
def mention
|
796
|
-
"<@&#{@id}>"
|
797
|
-
end
|
798
|
-
|
799
|
-
# @return [Array<Member>] an array of members who have this role.
|
800
|
-
# @note This requests a member chunk if it hasn't for the server before, which may be slow initially
|
801
|
-
def members
|
802
|
-
@server.members.select { |m| m.role? role }
|
803
|
-
end
|
804
|
-
|
805
|
-
alias_method :users, :members
|
806
|
-
|
807
|
-
# Updates the data cache from another Role object
|
808
|
-
# @note For internal use only
|
809
|
-
# @!visibility private
|
810
|
-
def update_from(other)
|
811
|
-
@permissions = other.permissions
|
812
|
-
@name = other.name
|
813
|
-
@hoist = other.hoist
|
814
|
-
@colour = other.colour
|
815
|
-
@position = other.position
|
816
|
-
end
|
817
|
-
|
818
|
-
# Updates the data cache from a hash containing data
|
819
|
-
# @note For internal use only
|
820
|
-
# @!visibility private
|
821
|
-
def update_data(new_data)
|
822
|
-
@name = new_data[:name] || new_data['name'] || @name
|
823
|
-
@hoist = new_data['hoist'] unless new_data['hoist'].nil?
|
824
|
-
@hoist = new_data[:hoist] unless new_data[:hoist].nil?
|
825
|
-
@colour = new_data[:colour] || (new_data['color'] ? ColourRGB.new(new_data['color']) : @colour)
|
826
|
-
end
|
827
|
-
|
828
|
-
# Sets the role name to something new
|
829
|
-
# @param name [String] The name that should be set
|
830
|
-
def name=(name)
|
831
|
-
update_role_data(name: name)
|
832
|
-
end
|
833
|
-
|
834
|
-
# Changes whether or not this role is displayed at the top of the user list
|
835
|
-
# @param hoist [true, false] The value it should be changed to
|
836
|
-
def hoist=(hoist)
|
837
|
-
update_role_data(hoist: hoist)
|
838
|
-
end
|
839
|
-
|
840
|
-
# Changes whether or not this role can be mentioned
|
841
|
-
# @param mentionable [true, false] The value it should be changed to
|
842
|
-
def mentionable=(mentionable)
|
843
|
-
update_role_data(mentionable: mentionable)
|
844
|
-
end
|
845
|
-
|
846
|
-
# Sets the role colour to something new
|
847
|
-
# @param colour [ColourRGB] The new colour
|
848
|
-
def colour=(colour)
|
849
|
-
update_role_data(colour: colour)
|
850
|
-
end
|
851
|
-
|
852
|
-
alias_method :color=, :colour=
|
853
|
-
|
854
|
-
# Changes this role's permissions to a fixed bitfield. This allows setting multiple permissions at once with just
|
855
|
-
# one API call.
|
856
|
-
#
|
857
|
-
# Information on how this bitfield is structured can be found at
|
858
|
-
# https://discordapp.com/developers/docs/topics/permissions.
|
859
|
-
# @example Remove all permissions from a role
|
860
|
-
# role.packed = 0
|
861
|
-
# @param packed [Integer] A bitfield with the desired permissions value.
|
862
|
-
# @param update_perms [true, false] Whether the internal data should also be updated. This should always be true
|
863
|
-
# when calling externally.
|
864
|
-
def packed=(packed, update_perms = true)
|
865
|
-
update_role_data(permissions: packed)
|
866
|
-
@permissions.bits = packed if update_perms
|
867
|
-
end
|
868
|
-
|
869
|
-
# Deletes this role. This cannot be undone without recreating the role!
|
870
|
-
def delete
|
871
|
-
API::Server.delete_role(@bot.token, @server.id, @id)
|
872
|
-
@server.delete_role(@id)
|
873
|
-
end
|
874
|
-
|
875
|
-
# The inspect method is overwritten to give more useful output
|
876
|
-
def inspect
|
877
|
-
"<Role name=#{@name} permissions=#{@permissions.inspect} hoist=#{@hoist} colour=#{@colour.inspect} server=#{@server.inspect}>"
|
878
|
-
end
|
879
|
-
|
880
|
-
private
|
881
|
-
|
882
|
-
def update_role_data(new_data)
|
883
|
-
API::Server.update_role(@bot.token, @server.id, @id,
|
884
|
-
new_data[:name] || @name,
|
885
|
-
(new_data[:colour] || @colour).combined,
|
886
|
-
new_data[:hoist].nil? ? @hoist : new_data[:hoist],
|
887
|
-
new_data[:mentionable].nil? ? @mentionable : new_data[:mentionable],
|
888
|
-
new_data[:permissions] || @permissions.bits)
|
889
|
-
update_data(new_data)
|
890
|
-
end
|
891
|
-
end
|
892
|
-
|
893
|
-
# A channel referenced by an invite. It has less data than regular channels, so it's a separate class
|
894
|
-
class InviteChannel
|
895
|
-
include IDObject
|
896
|
-
|
897
|
-
# @return [String] this channel's name.
|
898
|
-
attr_reader :name
|
899
|
-
|
900
|
-
# @return [Integer] this channel's type (0: text, 1: private, 2: voice, 3: group).
|
901
|
-
attr_reader :type
|
902
|
-
|
903
|
-
# @!visibility private
|
904
|
-
def initialize(data, bot)
|
905
|
-
@bot = bot
|
906
|
-
|
907
|
-
@id = data['id'].to_i
|
908
|
-
@name = data['name']
|
909
|
-
@type = data['type']
|
910
|
-
end
|
911
|
-
end
|
912
|
-
|
913
|
-
# A server referenced to by an invite
|
914
|
-
class InviteServer
|
915
|
-
include IDObject
|
916
|
-
|
917
|
-
# @return [String] this server's name.
|
918
|
-
attr_reader :name
|
919
|
-
|
920
|
-
# @return [String, nil] the hash of the server's invite splash screen (for partnered servers) or nil if none is
|
921
|
-
# present
|
922
|
-
attr_reader :splash_hash
|
923
|
-
|
924
|
-
# @!visibility private
|
925
|
-
def initialize(data, bot)
|
926
|
-
@bot = bot
|
927
|
-
|
928
|
-
@id = data['id'].to_i
|
929
|
-
@name = data['name']
|
930
|
-
@splash_hash = data['splash_hash']
|
931
|
-
end
|
932
|
-
end
|
933
|
-
|
934
|
-
# A Discord invite to a channel
|
935
|
-
class Invite
|
936
|
-
# @return [InviteChannel] the channel this invite references.
|
937
|
-
attr_reader :channel
|
938
|
-
|
939
|
-
# @return [InviteServer] the server this invite references.
|
940
|
-
attr_reader :server
|
941
|
-
|
942
|
-
# @return [Integer] the amount of uses left on this invite.
|
943
|
-
attr_reader :uses
|
944
|
-
alias_method :max_uses, :uses
|
945
|
-
|
946
|
-
# @return [User, nil] the user that made this invite. May also be nil if the user can't be determined.
|
947
|
-
attr_reader :inviter
|
948
|
-
alias_method :user, :inviter
|
949
|
-
|
950
|
-
# @return [true, false] whether or not this invite is temporary.
|
951
|
-
attr_reader :temporary
|
952
|
-
alias_method :temporary?, :temporary
|
953
|
-
|
954
|
-
# @return [true, false] whether this invite is still valid.
|
955
|
-
attr_reader :revoked
|
956
|
-
alias_method :revoked?, :revoked
|
957
|
-
|
958
|
-
# @return [String] this invite's code
|
959
|
-
attr_reader :code
|
960
|
-
|
961
|
-
# @!visibility private
|
962
|
-
def initialize(data, bot)
|
963
|
-
@bot = bot
|
964
|
-
|
965
|
-
@channel = InviteChannel.new(data['channel'], bot)
|
966
|
-
@server = InviteServer.new(data['guild'], bot)
|
967
|
-
@uses = data['uses']
|
968
|
-
@inviter = data['inviter'] ? (@bot.user(data['inviter']['id'].to_i) || User.new(data['inviter'], bot)) : nil
|
969
|
-
@temporary = data['temporary']
|
970
|
-
@revoked = data['revoked']
|
971
|
-
|
972
|
-
@code = data['code']
|
973
|
-
end
|
974
|
-
|
975
|
-
# Code based comparison
|
976
|
-
def ==(other)
|
977
|
-
other.respond_to?(:code) ? (@code == other.code) : (@code == other)
|
978
|
-
end
|
979
|
-
|
980
|
-
# Deletes this invite
|
981
|
-
def delete
|
982
|
-
API::Invite.delete(@bot.token, @code)
|
983
|
-
end
|
984
|
-
|
985
|
-
alias_method :revoke, :delete
|
986
|
-
|
987
|
-
# The inspect method is overwritten to give more useful output
|
988
|
-
def inspect
|
989
|
-
"<Invite code=#{@code} channel=#{@channel} uses=#{@uses} temporary=#{@temporary} revoked=#{@revoked}>"
|
990
|
-
end
|
991
|
-
|
992
|
-
# Creates an invite URL.
|
993
|
-
def url
|
994
|
-
"https://discord.gg/#{@code}"
|
995
|
-
end
|
996
|
-
end
|
997
|
-
|
998
|
-
# A Discord channel, including data like the topic
|
999
|
-
class Channel
|
1000
|
-
include IDObject
|
1001
|
-
|
1002
|
-
# @return [String] this channel's name.
|
1003
|
-
attr_reader :name
|
1004
|
-
|
1005
|
-
# @return [Server, nil] the server this channel is on. If this channel is a PM channel, it will be nil.
|
1006
|
-
attr_reader :server
|
1007
|
-
|
1008
|
-
# @return [Integer] the type of this channel (0: text, 1: private, 2: voice, 3: group)
|
1009
|
-
attr_reader :type
|
1010
|
-
|
1011
|
-
# @return [Integer, nil] the id of the owner of the group channel or nil if this is not a group channel.
|
1012
|
-
attr_reader :owner_id
|
1013
|
-
|
1014
|
-
# @return [Array<Recipient>, nil] the array of recipients of the private messages, or nil if this is not a Private channel
|
1015
|
-
attr_reader :recipients
|
1016
|
-
|
1017
|
-
# @return [String] the channel's topic
|
1018
|
-
attr_reader :topic
|
1019
|
-
|
1020
|
-
# @return [Integer] the bitrate (in bps) of the channel
|
1021
|
-
attr_reader :bitrate
|
1022
|
-
|
1023
|
-
# @return [Integer] the amount of users that can be in the channel. `0` means it is unlimited.
|
1024
|
-
attr_reader :user_limit
|
1025
|
-
alias_method :limit, :user_limit
|
1026
|
-
|
1027
|
-
# @return [Integer] the channel's position on the channel list
|
1028
|
-
attr_reader :position
|
1029
|
-
|
1030
|
-
# This channel's permission overwrites, represented as a hash of role/user ID to an OpenStruct which has the
|
1031
|
-
# `allow` and `deny` properties which are {Permissions} objects respectively.
|
1032
|
-
# @return [Hash<Integer => OpenStruct>] the channel's permission overwrites
|
1033
|
-
attr_reader :permission_overwrites
|
1034
|
-
|
1035
|
-
# @return [true, false] whether or not this channel is a PM or group channel.
|
1036
|
-
def private?
|
1037
|
-
pm? || group?
|
1038
|
-
end
|
1039
|
-
|
1040
|
-
# @return [String] a string that will mention the channel as a clickable link on Discord.
|
1041
|
-
def mention
|
1042
|
-
"<##{@id}>"
|
1043
|
-
end
|
1044
|
-
|
1045
|
-
# @return [Recipient, nil] the recipient of the private messages, or nil if this is not a PM channel
|
1046
|
-
def recipient
|
1047
|
-
@recipients.first if pm?
|
1048
|
-
end
|
1049
|
-
|
1050
|
-
# @!visibility private
|
1051
|
-
def initialize(data, bot, server = nil)
|
1052
|
-
@bot = bot
|
1053
|
-
# data is a sometimes a Hash and other times an array of Hashes, you only want the last one if it's an array
|
1054
|
-
data = data[-1] if data.is_a?(Array)
|
1055
|
-
|
1056
|
-
@id = data['id'].to_i
|
1057
|
-
@type = data['type'] || 0
|
1058
|
-
@topic = data['topic']
|
1059
|
-
@bitrate = data['bitrate']
|
1060
|
-
@user_limit = data['user_limit']
|
1061
|
-
@position = data['position']
|
1062
|
-
|
1063
|
-
if private?
|
1064
|
-
@recipients = []
|
1065
|
-
if data['recipients']
|
1066
|
-
data['recipients'].each do |recipient|
|
1067
|
-
recipient_user = bot.ensure_user(recipient)
|
1068
|
-
@recipients << Recipient.new(recipient_user, self, bot)
|
1069
|
-
end
|
1070
|
-
end
|
1071
|
-
if pm?
|
1072
|
-
@name = @recipients.first.username
|
1073
|
-
else
|
1074
|
-
@name = data['name']
|
1075
|
-
@owner_id = data['owner_id']
|
1076
|
-
end
|
1077
|
-
else
|
1078
|
-
@name = data['name']
|
1079
|
-
@server = if server
|
1080
|
-
server
|
1081
|
-
else
|
1082
|
-
bot.server(data['guild_id'].to_i)
|
1083
|
-
end
|
1084
|
-
end
|
1085
|
-
|
1086
|
-
# Populate permission overwrites
|
1087
|
-
@permission_overwrites = {}
|
1088
|
-
return unless data['permission_overwrites']
|
1089
|
-
data['permission_overwrites'].each do |element|
|
1090
|
-
role_id = element['id'].to_i
|
1091
|
-
deny = Permissions.new(element['deny'])
|
1092
|
-
allow = Permissions.new(element['allow'])
|
1093
|
-
@permission_overwrites[role_id] = OpenStruct.new
|
1094
|
-
@permission_overwrites[role_id].deny = deny
|
1095
|
-
@permission_overwrites[role_id].allow = allow
|
1096
|
-
end
|
1097
|
-
end
|
1098
|
-
|
1099
|
-
# @return [true, false] whether or not this channel is a text channel
|
1100
|
-
def text?
|
1101
|
-
@type.zero?
|
1102
|
-
end
|
1103
|
-
|
1104
|
-
# @return [true, false] whether or not this channel is a PM channel.
|
1105
|
-
def pm?
|
1106
|
-
@type == 1
|
1107
|
-
end
|
1108
|
-
|
1109
|
-
# @return [true, false] whether or not this channel is a voice channel.
|
1110
|
-
def voice?
|
1111
|
-
@type == 2
|
1112
|
-
end
|
1113
|
-
|
1114
|
-
# @return [true, false] whether or not this channel is a group channel.
|
1115
|
-
def group?
|
1116
|
-
@type == 3
|
1117
|
-
end
|
1118
|
-
|
1119
|
-
# Sends a message to this channel.
|
1120
|
-
# @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
|
1121
|
-
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
|
1122
|
-
# @return [Message] the message that was sent.
|
1123
|
-
def send_message(content, tts = false)
|
1124
|
-
@bot.send_message(@id, content, tts, @server && @server.id)
|
1125
|
-
end
|
1126
|
-
|
1127
|
-
alias_method :send, :send_message
|
1128
|
-
|
1129
|
-
# Sends a temporary message to this channel.
|
1130
|
-
# @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
|
1131
|
-
# @param timeout [Float] The amount of time in seconds after which the message sent will be deleted.
|
1132
|
-
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
|
1133
|
-
def send_temporary_message(content, timeout, tts = false)
|
1134
|
-
@bot.send_temporary_message(@id, content, timeout, tts, @server && @server.id)
|
1135
|
-
end
|
1136
|
-
|
1137
|
-
# Sends multiple messages to a channel
|
1138
|
-
# @param content [Array<String>] The messages to send.
|
1139
|
-
def send_multiple(content)
|
1140
|
-
content.each { |e| send_message(e) }
|
1141
|
-
end
|
1142
|
-
|
1143
|
-
# Splits a message into chunks whose length is at most the Discord character limit, then sends them individually.
|
1144
|
-
# Useful for sending long messages, but be wary of rate limits!
|
1145
|
-
def split_send(content)
|
1146
|
-
send_multiple(Discordrb.split_message(content))
|
1147
|
-
end
|
1148
|
-
|
1149
|
-
# Sends a file to this channel. If it is an image, it will be embedded.
|
1150
|
-
# @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)
|
1151
|
-
# @param caption [string] The caption for the file.
|
1152
|
-
# @param tts [true, false] Whether or not this file's caption should be sent using Discord text-to-speech.
|
1153
|
-
def send_file(file, caption: nil, tts: false)
|
1154
|
-
@bot.send_file(@id, file, caption: caption, tts: tts)
|
1155
|
-
end
|
1156
|
-
|
1157
|
-
# Permanently deletes this channel
|
1158
|
-
def delete
|
1159
|
-
API::Channel.delete(@bot.token, @id)
|
1160
|
-
end
|
1161
|
-
|
1162
|
-
# Sets this channel's name. The name must be alphanumeric with dashes, unless this is a voice channel (then there are no limitations)
|
1163
|
-
# @param name [String] The new name.
|
1164
|
-
def name=(name)
|
1165
|
-
@name = name
|
1166
|
-
update_channel_data
|
1167
|
-
end
|
1168
|
-
|
1169
|
-
# Sets this channel's topic.
|
1170
|
-
# @param topic [String] The new topic.
|
1171
|
-
def topic=(topic)
|
1172
|
-
raise 'Tried to set topic on voice channel' if voice?
|
1173
|
-
@topic = topic
|
1174
|
-
update_channel_data
|
1175
|
-
end
|
1176
|
-
|
1177
|
-
# Sets this channel's bitrate.
|
1178
|
-
# @param bitrate [Integer] The new bitrate (in bps). Number has to be between 8000-96000 (128000 for VIP servers)
|
1179
|
-
def bitrate=(bitrate)
|
1180
|
-
raise 'Tried to set bitrate on text channel' if text?
|
1181
|
-
@bitrate = bitrate
|
1182
|
-
update_channel_data
|
1183
|
-
end
|
1184
|
-
|
1185
|
-
# Sets this channel's user limit.
|
1186
|
-
# @param limit [Integer] The new user limit. `0` for unlimited, has to be a number between 0-99
|
1187
|
-
def user_limit=(limit)
|
1188
|
-
raise 'Tried to set user_limit on text channel' if text?
|
1189
|
-
@user_limit = limit
|
1190
|
-
update_channel_data
|
1191
|
-
end
|
1192
|
-
|
1193
|
-
alias_method :limit=, :user_limit=
|
1194
|
-
|
1195
|
-
# Sets this channel's position in the list.
|
1196
|
-
# @param position [Integer] The new position.
|
1197
|
-
def position=(position)
|
1198
|
-
@position = position
|
1199
|
-
update_channel_data
|
1200
|
-
end
|
1201
|
-
|
1202
|
-
# Defines a permission overwrite for this channel that sets the specified thing to the specified allow and deny
|
1203
|
-
# permission sets, or change an existing one.
|
1204
|
-
# @param thing [User, Role] What to define an overwrite for.
|
1205
|
-
# @param allow [#bits, Permissions, Integer] The permission sets that should receive an `allow` override (i. e. a
|
1206
|
-
# green checkmark on Discord)
|
1207
|
-
# @param deny [#bits, Permissions, Integer] The permission sets that should receive a `deny` override (i. e. a red
|
1208
|
-
# cross on Discord)
|
1209
|
-
# @example Define a permission overwrite for a user that can then mention everyone and use TTS, but not create any invites
|
1210
|
-
# allow = Discordrb::Permissions.new
|
1211
|
-
# allow.can_mention_everyone = true
|
1212
|
-
# allow.can_send_tts_messages = true
|
1213
|
-
#
|
1214
|
-
# deny = Discordrb::Permissions.new
|
1215
|
-
# deny.can_create_instant_invite = true
|
1216
|
-
#
|
1217
|
-
# channel.define_overwrite(user, allow, deny)
|
1218
|
-
def define_overwrite(thing, allow, deny)
|
1219
|
-
allow_bits = allow.respond_to?(:bits) ? allow.bits : allow
|
1220
|
-
deny_bits = deny.respond_to?(:bits) ? deny.bits : deny
|
1221
|
-
|
1222
|
-
type = if thing.is_a?(User) || thing.is_a?(Member) || thing.is_a?(Recipient) || thing.is_a?(Profile)
|
1223
|
-
:member
|
1224
|
-
elsif thing.is_a? Role
|
1225
|
-
:role
|
1226
|
-
else
|
1227
|
-
raise ArgumentError, '`thing` in define_overwrite needs to be a kind of User (User, Member, Recipient, Profile) or a Role!'
|
1228
|
-
end
|
1229
|
-
|
1230
|
-
API::Channel.update_permission(@bot.token, @id, thing.id, allow_bits, deny_bits, type)
|
1231
|
-
end
|
1232
|
-
|
1233
|
-
# Updates the cached data from another channel.
|
1234
|
-
# @note For internal use only
|
1235
|
-
# @!visibility private
|
1236
|
-
def update_from(other)
|
1237
|
-
@topic = other.topic
|
1238
|
-
@name = other.name
|
1239
|
-
@permission_overwrites = other.permission_overwrites
|
1240
|
-
end
|
1241
|
-
|
1242
|
-
# The list of users currently in this channel. For a voice channel, it will return all the members currently
|
1243
|
-
# in that channel. For a text channel, it will return all online members that have permission to read it.
|
1244
|
-
# @return [Array<Member>] the users in this channel
|
1245
|
-
def users
|
1246
|
-
if text?
|
1247
|
-
@server.online_members(include_idle: true).select { |u| u.can_read_messages? self }
|
1248
|
-
elsif voice?
|
1249
|
-
@server.voice_states.map { |id, voice_state| @server.member(id) if !voice_state.voice_channel.nil? && voice_state.voice_channel.id == @id }.compact
|
1250
|
-
end
|
1251
|
-
end
|
1252
|
-
|
1253
|
-
# Retrieves some of this channel's message history.
|
1254
|
-
# @param amount [Integer] How many messages to retrieve. This must be less than or equal to 100, if it is higher
|
1255
|
-
# than 100 it will be treated as 100 on Discord's side.
|
1256
|
-
# @param before_id [Integer] The ID of the most recent message the retrieval should start at, or nil if it should
|
1257
|
-
# start at the current message.
|
1258
|
-
# @param after_id [Integer] The ID of the oldest message the retrieval should start at, or nil if it should start
|
1259
|
-
# as soon as possible with the specified amount.
|
1260
|
-
# @example Count the number of messages in the last 50 messages that contain the letter 'e'.
|
1261
|
-
# message_count = channel.history(50).count {|message| message.content.include? "e"}
|
1262
|
-
# @return [Array<Message>] the retrieved messages.
|
1263
|
-
def history(amount, before_id = nil, after_id = nil)
|
1264
|
-
logs = API::Channel.messages(@bot.token, @id, amount, before_id, after_id)
|
1265
|
-
JSON.parse(logs).map { |message| Message.new(message, @bot) }
|
1266
|
-
end
|
1267
|
-
|
1268
|
-
# Retrieves message history, but only message IDs for use with prune
|
1269
|
-
# @note For internal use only
|
1270
|
-
# @!visibility private
|
1271
|
-
def history_ids(amount, before_id = nil, after_id = nil)
|
1272
|
-
logs = API::Channel.messages(@bot.token, @id, amount, before_id, after_id)
|
1273
|
-
JSON.parse(logs).map { |message| message['id'] }
|
1274
|
-
end
|
1275
|
-
|
1276
|
-
# Returns a single message from this channel's history by ID.
|
1277
|
-
# @param message_id [Integer] The ID of the message to retrieve.
|
1278
|
-
# @return [Message] the retrieved message.
|
1279
|
-
def load_message(message_id)
|
1280
|
-
response = API::Channel.message(@bot.token, @id, message_id)
|
1281
|
-
return Message.new(JSON.parse(response), @bot)
|
1282
|
-
rescue RestClient::ResourceNotFound
|
1283
|
-
return nil
|
1284
|
-
end
|
1285
|
-
|
1286
|
-
alias_method :message, :load_message
|
1287
|
-
|
1288
|
-
# Requests all pinned messages of a channel.
|
1289
|
-
# @return [Array<Message>] the received messages.
|
1290
|
-
def pins
|
1291
|
-
msgs = API::Channel.pinned_messages(@bot.token, @id)
|
1292
|
-
JSON.parse(msgs).map { |msg| Message.new(msg, @bot) }
|
1293
|
-
end
|
1294
|
-
|
1295
|
-
# Delete the last N messages on this channel.
|
1296
|
-
# @param amount [Integer] How many messages to delete. Must be a value between 2 and 100 (Discord limitation)
|
1297
|
-
# @raise [ArgumentError] if the amount of messages is not a value between 2 and 100
|
1298
|
-
def prune(amount)
|
1299
|
-
raise ArgumentError, 'Can only prune between 2 and 100 messages!' unless amount.between?(2, 100)
|
1300
|
-
|
1301
|
-
messages = history_ids(amount)
|
1302
|
-
API::Channel.bulk_delete_messages(@bot.token, @id, messages)
|
1303
|
-
end
|
1304
|
-
|
1305
|
-
# Deletes a collection of messages
|
1306
|
-
# @param messages [Array<Message, Integer>] the messages (or message IDs) to delete. Total must be an amount between 2 and 100 (Discord limitation)
|
1307
|
-
# @raise [ArgumentError] if the amount of messages is not a value between 2 and 100
|
1308
|
-
def delete_messages(messages)
|
1309
|
-
raise ArgumentError, 'Can only delete between 2 and 100 messages!' unless messages.count.between?(2, 100)
|
1310
|
-
|
1311
|
-
messages.map!(&:resolve_id)
|
1312
|
-
API::Channel.bulk_delete_messages(@bot.token, @id, messages)
|
1313
|
-
end
|
1314
|
-
|
1315
|
-
# Updates the cached permission overwrites
|
1316
|
-
# @note For internal use only
|
1317
|
-
# @!visibility private
|
1318
|
-
def update_overwrites(overwrites)
|
1319
|
-
@permission_overwrites = overwrites
|
1320
|
-
end
|
1321
|
-
|
1322
|
-
# Add an {Await} for a message in this channel. This is identical in functionality to adding a
|
1323
|
-
# {Discordrb::Events::MessageEvent} await with the `in` attribute as this channel.
|
1324
|
-
# @see Bot#add_await
|
1325
|
-
def await(key, attributes = {}, &block)
|
1326
|
-
@bot.add_await(key, Discordrb::Events::MessageEvent, { in: @id }.merge(attributes), &block)
|
1327
|
-
end
|
1328
|
-
|
1329
|
-
# Creates a new invite to this channel.
|
1330
|
-
# @param max_age [Integer] How many seconds this invite should last.
|
1331
|
-
# @param max_uses [Integer] How many times this invite should be able to be used.
|
1332
|
-
# @param temporary [true, false] Whether membership should be temporary (kicked after going offline).
|
1333
|
-
# @return [Invite] the created invite.
|
1334
|
-
def make_invite(max_age = 0, max_uses = 0, temporary = false)
|
1335
|
-
response = API::Channel.create_invite(@bot.token, @id, max_age, max_uses, temporary)
|
1336
|
-
Invite.new(JSON.parse(response), @bot)
|
1337
|
-
end
|
1338
|
-
|
1339
|
-
alias_method :invite, :make_invite
|
1340
|
-
|
1341
|
-
# Starts typing, which displays the typing indicator on the client for five seconds.
|
1342
|
-
# If you want to keep typing you'll have to resend this every five seconds. (An abstraction
|
1343
|
-
# for this will eventually be coming)
|
1344
|
-
def start_typing
|
1345
|
-
API::Channel.start_typing(@bot.token, @id)
|
1346
|
-
end
|
1347
|
-
|
1348
|
-
# Creates a Group channel
|
1349
|
-
# @param user_ids [Array<Integer>] Array of user IDs to add to the new group channel (Excluding
|
1350
|
-
# the recipient of the PM channel).
|
1351
|
-
# @return [Channel] the created channel.
|
1352
|
-
def create_group(user_ids)
|
1353
|
-
raise 'Attempted to create group channel on a non-pm channel!' unless pm?
|
1354
|
-
response = API::Channel.create_group(@bot.token, @id, user_ids.shift)
|
1355
|
-
channel = Channel.new(JSON.parse(response), @bot)
|
1356
|
-
channel.add_group_users(user_ids)
|
1357
|
-
end
|
1358
|
-
|
1359
|
-
# Adds a user to a Group channel
|
1360
|
-
# @param user_ids [Array<#resolve_id>, #resolve_id] User ID or array of user IDs to add to the group channel.
|
1361
|
-
# @return [Channel] the group channel.
|
1362
|
-
def add_group_users(user_ids)
|
1363
|
-
raise 'Attempted to add a user to a non-group channel!' unless group?
|
1364
|
-
user_ids = [user_ids] unless user_ids.is_a? Array
|
1365
|
-
user_ids.each do |user_id|
|
1366
|
-
API::Channel.add_group_user(@bot.token, @id, user_id.resolve_id)
|
1367
|
-
end
|
1368
|
-
self
|
1369
|
-
end
|
1370
|
-
|
1371
|
-
alias_method :add_group_user, :add_group_users
|
1372
|
-
|
1373
|
-
# Removes a user from a group channel.
|
1374
|
-
# @param user_ids [Array<#resolve_id>, #resolve_id] User ID or array of user IDs to remove from the group channel.
|
1375
|
-
# @return [Channel] the group channel.
|
1376
|
-
def remove_group_users(user_ids)
|
1377
|
-
raise 'Attempted to remove a user from a non-group channel!' unless group?
|
1378
|
-
user_ids = [user_ids] unless user_ids.is_a? Array
|
1379
|
-
user_ids.each do |user_id|
|
1380
|
-
API::Channel.remove_group_user(@bot.token, @id, user_id.resolve_id)
|
1381
|
-
end
|
1382
|
-
self
|
1383
|
-
end
|
1384
|
-
|
1385
|
-
alias_method :remove_group_user, :remove_group_users
|
1386
|
-
|
1387
|
-
# Leaves the group
|
1388
|
-
def leave_group
|
1389
|
-
raise 'Attempted to leave a non-group channel!' unless group?
|
1390
|
-
API::Channel.leave_group(@bot.token, @id)
|
1391
|
-
end
|
1392
|
-
|
1393
|
-
alias_method :leave, :leave_group
|
1394
|
-
|
1395
|
-
# The inspect method is overwritten to give more useful output
|
1396
|
-
def inspect
|
1397
|
-
"<Channel name=#{@name} id=#{@id} topic=\"#{@topic}\" type=#{@type} position=#{@position} server=#{@server}>"
|
1398
|
-
end
|
1399
|
-
|
1400
|
-
# Adds a recipient to a group channel.
|
1401
|
-
# @param recipient [Recipient] the recipient to add to the group
|
1402
|
-
# @raise [ArgumentError] if tried to add a non-recipient
|
1403
|
-
# @note For internal use only
|
1404
|
-
# @!visibility private
|
1405
|
-
def add_recipient(recipient)
|
1406
|
-
raise 'Tried to add recipient to a non-group channel' unless group?
|
1407
|
-
raise ArgumentError, 'Tried to add a non-recipient to a group' unless recipient.is_a?(Recipient)
|
1408
|
-
@recipients << recipient
|
1409
|
-
end
|
1410
|
-
|
1411
|
-
# Removes a recipient from a group channel.
|
1412
|
-
# @param recipient [Recipient] the recipient to remove from the group
|
1413
|
-
# @raise [ArgumentError] if tried to remove a non-recipient
|
1414
|
-
# @note For internal use only
|
1415
|
-
# @!visibility private
|
1416
|
-
def remove_recipient(recipient)
|
1417
|
-
raise 'Tried to add recipient to a non-group channel' unless group?
|
1418
|
-
raise ArgumentError, 'Tried to remove a non-recipient from a group' unless recipient.is_a?(Recipient)
|
1419
|
-
@recipients.delete(recipient)
|
1420
|
-
end
|
1421
|
-
|
1422
|
-
private
|
1423
|
-
|
1424
|
-
def update_channel_data
|
1425
|
-
API::Channel.update(@bot.token, @id, @name, @topic, @position, @bitrate, @user_limit)
|
1426
|
-
end
|
1427
|
-
end
|
1428
|
-
|
1429
|
-
# An Embed object that is contained in a message
|
1430
|
-
# A freshly generated embed object will not appear in a message object
|
1431
|
-
# unless grabbed from its ID in a channel.
|
1432
|
-
class Embed
|
1433
|
-
# @return [Message] the message this embed object is contained in.
|
1434
|
-
attr_reader :message
|
1435
|
-
|
1436
|
-
# @return [String] the URL this embed object is based on.
|
1437
|
-
attr_reader :url
|
1438
|
-
|
1439
|
-
# @return [String, nil] the title of the embed object. `nil` if there is not a title
|
1440
|
-
attr_reader :title
|
1441
|
-
|
1442
|
-
# @return [String, nil] the description of the embed object. `nil` if there is not a description
|
1443
|
-
attr_reader :description
|
1444
|
-
|
1445
|
-
# @return [Symbol] the type of the embed object. Possible types are:
|
1446
|
-
#
|
1447
|
-
# * `:link`
|
1448
|
-
# * `:video`
|
1449
|
-
# * `:image`
|
1450
|
-
attr_reader :type
|
1451
|
-
|
1452
|
-
# @return [EmbedProvider, nil] the provider of the embed object. `nil` is there is not a provider
|
1453
|
-
attr_reader :provider
|
1454
|
-
|
1455
|
-
# @return [EmbedThumbnail, nil] the thumbnail of the embed object. `nil` is there is not a thumbnail
|
1456
|
-
attr_reader :thumbnail
|
1457
|
-
|
1458
|
-
# @return [EmbedAuthor, nil] the author of the embed object. `nil` is there is not an author
|
1459
|
-
attr_reader :author
|
1460
|
-
|
1461
|
-
# @!visibility private
|
1462
|
-
def initialize(data, message)
|
1463
|
-
@message = message
|
1464
|
-
|
1465
|
-
@url = data['url']
|
1466
|
-
@title = data['title']
|
1467
|
-
@type = data['type'].to_sym
|
1468
|
-
@description = data['description']
|
1469
|
-
@provider = data['provider'].nil? ? nil : EmbedProvider.new(data['provider'], self)
|
1470
|
-
@thumbnail = data['thumbnail'].nil? ? nil : EmbedThumbnail.new(data['thumbnail'], self)
|
1471
|
-
@author = data['author'].nil? ? nil : EmbedAuthor.new(data['author'], self)
|
1472
|
-
end
|
1473
|
-
end
|
1474
|
-
|
1475
|
-
# An Embed thumbnail for the embed object
|
1476
|
-
class EmbedThumbnail
|
1477
|
-
# @return [Embed] the embed object this is based on.
|
1478
|
-
attr_reader :embed
|
1479
|
-
|
1480
|
-
# @return [String] the CDN URL this thumbnail can be downloaded at.
|
1481
|
-
attr_reader :url
|
1482
|
-
|
1483
|
-
# @return [String] the thumbnail's proxy URL - I'm not sure what exactly this does, but I think it has something to
|
1484
|
-
# do with CDNs
|
1485
|
-
attr_reader :proxy_url
|
1486
|
-
|
1487
|
-
# @return [Integer] the width of this thumbnail file, in pixels.
|
1488
|
-
attr_reader :width
|
1489
|
-
|
1490
|
-
# @return [Integer] the height of this thumbnail file, in pixels.
|
1491
|
-
attr_reader :height
|
1492
|
-
|
1493
|
-
# @!visibility private
|
1494
|
-
def initialize(data, embed)
|
1495
|
-
@embed = embed
|
1496
|
-
|
1497
|
-
@url = data['url']
|
1498
|
-
@proxy_url = data['proxy_url']
|
1499
|
-
@width = data['width']
|
1500
|
-
@height = data['height']
|
1501
|
-
end
|
1502
|
-
end
|
1503
|
-
|
1504
|
-
# An Embed provider for the embed object
|
1505
|
-
class EmbedProvider
|
1506
|
-
# @return [Embed] the embed object this is based on.
|
1507
|
-
attr_reader :embed
|
1508
|
-
|
1509
|
-
# @return [String] the provider's name.
|
1510
|
-
attr_reader :name
|
1511
|
-
|
1512
|
-
# @return [String, nil] the URL of the provider. `nil` is there is no URL
|
1513
|
-
attr_reader :url
|
1514
|
-
|
1515
|
-
# @!visibility private
|
1516
|
-
def initialize(data, embed)
|
1517
|
-
@embed = embed
|
1518
|
-
|
1519
|
-
@name = data['name']
|
1520
|
-
@url = data['url']
|
1521
|
-
end
|
1522
|
-
end
|
1523
|
-
|
1524
|
-
# An Embed author for the embed object
|
1525
|
-
class EmbedAuthor
|
1526
|
-
# @return [Embed] the embed object this is based on.
|
1527
|
-
attr_reader :embed
|
1528
|
-
|
1529
|
-
# @return [String] the author's name.
|
1530
|
-
attr_reader :name
|
1531
|
-
|
1532
|
-
# @return [String, nil] the URL of the author's website. `nil` is there is no URL
|
1533
|
-
attr_reader :url
|
1534
|
-
|
1535
|
-
# @!visibility private
|
1536
|
-
def initialize(data, embed)
|
1537
|
-
@embed = embed
|
1538
|
-
|
1539
|
-
@name = data['name']
|
1540
|
-
@url = data['url']
|
1541
|
-
end
|
1542
|
-
end
|
1543
|
-
|
1544
|
-
# An attachment to a message
|
1545
|
-
class Attachment
|
1546
|
-
include IDObject
|
1547
|
-
|
1548
|
-
# @return [Message] the message this attachment belongs to.
|
1549
|
-
attr_reader :message
|
1550
|
-
|
1551
|
-
# @return [String] the CDN URL this attachment can be downloaded at.
|
1552
|
-
attr_reader :url
|
1553
|
-
|
1554
|
-
# @return [String] the attachment's proxy URL - I'm not sure what exactly this does, but I think it has something to
|
1555
|
-
# do with CDNs
|
1556
|
-
attr_reader :proxy_url
|
1557
|
-
|
1558
|
-
# @return [String] the attachment's filename.
|
1559
|
-
attr_reader :filename
|
1560
|
-
|
1561
|
-
# @return [Integer] the attachment's file size in bytes.
|
1562
|
-
attr_reader :size
|
1563
|
-
|
1564
|
-
# @return [Integer, nil] the width of an image file, in pixels, or nil if the file is not an image.
|
1565
|
-
attr_reader :width
|
1566
|
-
|
1567
|
-
# @return [Integer, nil] the height of an image file, in pixels, or nil if the file is not an image.
|
1568
|
-
attr_reader :height
|
1569
|
-
|
1570
|
-
# @!visibility private
|
1571
|
-
def initialize(data, message, bot)
|
1572
|
-
@bot = bot
|
1573
|
-
@message = message
|
1574
|
-
|
1575
|
-
@url = data['url']
|
1576
|
-
@proxy_url = data['proxy_url']
|
1577
|
-
@filename = data['filename']
|
1578
|
-
|
1579
|
-
@size = data['size']
|
1580
|
-
|
1581
|
-
@width = data['width']
|
1582
|
-
@height = data['height']
|
1583
|
-
end
|
1584
|
-
|
1585
|
-
# @return [true, false] whether this file is an image file.
|
1586
|
-
def image?
|
1587
|
-
!(@width.nil? || @height.nil?)
|
1588
|
-
end
|
1589
|
-
end
|
1590
|
-
|
1591
|
-
# A message on Discord that was sent to a text channel
|
1592
|
-
class Message
|
1593
|
-
include IDObject
|
1594
|
-
|
1595
|
-
# @return [String] the content of this message.
|
1596
|
-
attr_reader :content
|
1597
|
-
alias_method :text, :content
|
1598
|
-
alias_method :to_s, :content
|
1599
|
-
|
1600
|
-
# @return [Member] the user that sent this message.
|
1601
|
-
attr_reader :author
|
1602
|
-
alias_method :user, :author
|
1603
|
-
alias_method :writer, :author
|
1604
|
-
|
1605
|
-
# @return [Channel] the channel in which this message was sent.
|
1606
|
-
attr_reader :channel
|
1607
|
-
|
1608
|
-
# @return [Time] the timestamp at which this message was sent.
|
1609
|
-
attr_reader :timestamp
|
1610
|
-
|
1611
|
-
# @return [Time] the timestamp at which this message was edited. `nil` if the message was never edited.
|
1612
|
-
attr_reader :edited_timestamp
|
1613
|
-
alias_method :edit_timestamp, :edited_timestamp
|
1614
|
-
|
1615
|
-
# @return [Array<User>] the users that were mentioned in this message.
|
1616
|
-
attr_reader :mentions
|
1617
|
-
|
1618
|
-
# @return [Array<Role>] the roles that were mentioned in this message.
|
1619
|
-
attr_reader :role_mentions
|
1620
|
-
|
1621
|
-
# @return [Array<Attachment>] the files attached to this message.
|
1622
|
-
attr_reader :attachments
|
1623
|
-
|
1624
|
-
# @return [Array<Embed>] the embed objects contained in this message.
|
1625
|
-
attr_reader :embeds
|
1626
|
-
|
1627
|
-
# @return [true, false] whether the message used Text-To-Speech (TTS) or not.
|
1628
|
-
attr_reader :tts
|
1629
|
-
alias_method :tts?, :tts
|
1630
|
-
|
1631
|
-
# @return [String] used for validating a message was sent
|
1632
|
-
attr_reader :nonce
|
1633
|
-
|
1634
|
-
# @return [true, false] whether the message was edited or not.
|
1635
|
-
attr_reader :edited
|
1636
|
-
alias_method :edited?, :edited
|
1637
|
-
|
1638
|
-
# @return [true, false] whether the message mentioned everyone or not.
|
1639
|
-
attr_reader :mention_everyone
|
1640
|
-
alias_method :mention_everyone?, :mention_everyone
|
1641
|
-
alias_method :mentions_everyone?, :mention_everyone
|
1642
|
-
|
1643
|
-
# @return [true, false] whether the message is pinned or not.
|
1644
|
-
attr_reader :pinned
|
1645
|
-
alias_method :pinned?, :pinned
|
1646
|
-
|
1647
|
-
# @return [Integer, nil] the webhook ID that sent this message, or nil if it wasn't sent through a webhook.
|
1648
|
-
attr_reader :webhook_id
|
1649
|
-
|
1650
|
-
# The discriminator that webhook user accounts have.
|
1651
|
-
ZERO_DISCRIM = '0000'.freeze
|
1652
|
-
|
1653
|
-
# @!visibility private
|
1654
|
-
def initialize(data, bot)
|
1655
|
-
@bot = bot
|
1656
|
-
@content = data['content']
|
1657
|
-
@channel = bot.channel(data['channel_id'].to_i)
|
1658
|
-
@pinned = data['pinned']
|
1659
|
-
@tts = data['tts']
|
1660
|
-
@nonce = data['nonce']
|
1661
|
-
@mention_everyone = data['mention_everyone']
|
1662
|
-
|
1663
|
-
@author = if data['author']
|
1664
|
-
if data['author']['discriminator'] == ZERO_DISCRIM
|
1665
|
-
# This is a webhook user! It would be pointless to try to resolve a member here, so we just create
|
1666
|
-
# a User and return that instead.
|
1667
|
-
Discordrb::LOGGER.debug("Webhook user: #{data['author']['id']}")
|
1668
|
-
User.new(data['author'], @bot)
|
1669
|
-
elsif @channel.private?
|
1670
|
-
# Turn the message user into a recipient - we can't use the channel recipient
|
1671
|
-
# directly because the bot may also send messages to the channel
|
1672
|
-
Recipient.new(bot.user(data['author']['id'].to_i), @channel, bot)
|
1673
|
-
else
|
1674
|
-
member = @channel.server.member(data['author']['id'].to_i)
|
1675
|
-
Discordrb::LOGGER.warn("Member with ID #{data['author']['id']} not cached even though it should be.") unless member
|
1676
|
-
member
|
1677
|
-
end
|
1678
|
-
end
|
1679
|
-
|
1680
|
-
@webhook_id = data['webhook_id'].to_i if data['webhook_id']
|
1681
|
-
|
1682
|
-
@timestamp = Time.parse(data['timestamp']) if data['timestamp']
|
1683
|
-
@edited_timestamp = data['edited_timestamp'].nil? ? nil : Time.parse(data['edited_timestamp'])
|
1684
|
-
@edited = !@edited_timestamp.nil?
|
1685
|
-
@id = data['id'].to_i
|
1686
|
-
|
1687
|
-
@emoji = []
|
1688
|
-
|
1689
|
-
@mentions = []
|
1690
|
-
|
1691
|
-
data['mentions'].each do |element|
|
1692
|
-
@mentions << bot.ensure_user(element)
|
1693
|
-
end if data['mentions']
|
1694
|
-
|
1695
|
-
@role_mentions = []
|
1696
|
-
|
1697
|
-
# Role mentions can only happen on public servers so make sure we only parse them there
|
1698
|
-
if @channel.text?
|
1699
|
-
data['mention_roles'].each do |element|
|
1700
|
-
@role_mentions << @channel.server.role(element.to_i)
|
1701
|
-
end if data['mention_roles']
|
1702
|
-
end
|
1703
|
-
|
1704
|
-
@attachments = []
|
1705
|
-
@attachments = data['attachments'].map { |e| Attachment.new(e, self, @bot) } if data['attachments']
|
1706
|
-
|
1707
|
-
@embeds = []
|
1708
|
-
@embeds = data['embeds'].map { |e| Embed.new(e, self) } if data['embeds']
|
1709
|
-
end
|
1710
|
-
|
1711
|
-
# Replies to this message with the specified content.
|
1712
|
-
# @see Channel#send_message
|
1713
|
-
def reply(content)
|
1714
|
-
@channel.send_message(content)
|
1715
|
-
end
|
1716
|
-
|
1717
|
-
# Edits this message to have the specified content instead.
|
1718
|
-
# You can only edit your own messages.
|
1719
|
-
# @param new_content [String] the new content the message should have.
|
1720
|
-
# @return [Message] the resulting message.
|
1721
|
-
def edit(new_content)
|
1722
|
-
response = API::Channel.edit_message(@bot.token, @channel.id, @id, new_content)
|
1723
|
-
Message.new(JSON.parse(response), @bot)
|
1724
|
-
end
|
1725
|
-
|
1726
|
-
# Deletes this message.
|
1727
|
-
def delete
|
1728
|
-
API::Channel.delete_message(@bot.token, @channel.id, @id)
|
1729
|
-
nil
|
1730
|
-
end
|
1731
|
-
|
1732
|
-
# Pins this message
|
1733
|
-
def pin
|
1734
|
-
API::Channel.pin_message(@bot.token, @channel.id, @id)
|
1735
|
-
@pinned = true
|
1736
|
-
nil
|
1737
|
-
end
|
1738
|
-
|
1739
|
-
# Unpins this message
|
1740
|
-
def unpin
|
1741
|
-
API::Channel.unpin_message(@bot.token, @channel.id, @id)
|
1742
|
-
@pinned = false
|
1743
|
-
nil
|
1744
|
-
end
|
1745
|
-
|
1746
|
-
# Add an {Await} for a message with the same user and channel.
|
1747
|
-
# @see Bot#add_await
|
1748
|
-
def await(key, attributes = {}, &block)
|
1749
|
-
@bot.add_await(key, Discordrb::Events::MessageEvent, { from: @author.id, in: @channel.id }.merge(attributes), &block)
|
1750
|
-
end
|
1751
|
-
|
1752
|
-
# @return [true, false] whether this message was sent by the current {Bot}.
|
1753
|
-
def from_bot?
|
1754
|
-
@author && @author.current_bot?
|
1755
|
-
end
|
1756
|
-
|
1757
|
-
# @return [true, false] whether this message has been sent over a webhook.
|
1758
|
-
def webhook?
|
1759
|
-
!@webhook_id.nil?
|
1760
|
-
end
|
1761
|
-
|
1762
|
-
# @!visibility private
|
1763
|
-
# @return [Array<String>] the emoji mentions found in the message
|
1764
|
-
def scan_for_emoji
|
1765
|
-
emoji = @content.split
|
1766
|
-
emoji = emoji.grep(/<:(?<name>\w+):(?<id>\d+)>?/)
|
1767
|
-
emoji
|
1768
|
-
end
|
1769
|
-
|
1770
|
-
# @return [Array<Emoji>] the emotes that were used/mentioned in this message (Only returns Emoji the bot has access to, else nil).
|
1771
|
-
def emoji
|
1772
|
-
return if @content.nil?
|
1773
|
-
|
1774
|
-
emoji = scan_for_emoji
|
1775
|
-
emoji.each do |element|
|
1776
|
-
@emoji << @bot.parse_mention(element)
|
1777
|
-
end
|
1778
|
-
@emoji
|
1779
|
-
end
|
1780
|
-
|
1781
|
-
# Check if any emoji got used in this message
|
1782
|
-
# @return [true, false] whether or not any emoji got used
|
1783
|
-
def emoji?
|
1784
|
-
emoji = scan_for_emoji
|
1785
|
-
return true unless emoji.empty?
|
1786
|
-
end
|
1787
|
-
|
1788
|
-
# The inspect method is overwritten to give more useful output
|
1789
|
-
def inspect
|
1790
|
-
"<Message content=\"#{@content}\" id=#{@id} timestamp=#{@timestamp} author=#{@author} channel=#{@channel}>"
|
1791
|
-
end
|
1792
|
-
end
|
1793
|
-
|
1794
|
-
# Server emoji
|
1795
|
-
class Emoji
|
1796
|
-
include IDObject
|
1797
|
-
|
1798
|
-
# @return [String] the emoji name
|
1799
|
-
attr_reader :name
|
1800
|
-
|
1801
|
-
# @return [Server] the server of this emoji
|
1802
|
-
attr_reader :server
|
1803
|
-
|
1804
|
-
# @return [Array<Role>] roles this emoji is active for
|
1805
|
-
attr_reader :roles
|
1806
|
-
|
1807
|
-
def initialize(data, bot, server)
|
1808
|
-
@bot = bot
|
1809
|
-
@roles = nil
|
1810
|
-
|
1811
|
-
@name = data['name']
|
1812
|
-
@server = server
|
1813
|
-
@id = data['id'].to_i
|
1814
|
-
|
1815
|
-
process_roles(data['roles']) if server
|
1816
|
-
end
|
1817
|
-
|
1818
|
-
# @return [String] the layout to mention it (or have it used) in a message
|
1819
|
-
def mention
|
1820
|
-
"<:#{@name}:#{@id}>"
|
1821
|
-
end
|
1822
|
-
|
1823
|
-
alias_method :use, :mention
|
1824
|
-
alias_method :to_s, :mention
|
1825
|
-
|
1826
|
-
# @return [String] the icon URL of the emoji
|
1827
|
-
def icon_url
|
1828
|
-
API.emoji_icon_url(@id)
|
1829
|
-
end
|
1830
|
-
|
1831
|
-
# The inspect method is overwritten to give more useful output
|
1832
|
-
def inspect
|
1833
|
-
"<Emoji name=#{@name} id=#{@id}>"
|
1834
|
-
end
|
1835
|
-
|
1836
|
-
# @!visibility private
|
1837
|
-
def process_roles(roles)
|
1838
|
-
@roles = []
|
1839
|
-
return unless roles
|
1840
|
-
roles.each do |role_id|
|
1841
|
-
role = server.role(role_id)
|
1842
|
-
@roles << role
|
1843
|
-
end
|
1844
|
-
end
|
1845
|
-
end
|
1846
|
-
|
1847
|
-
# Emoji that is not tailored to a server
|
1848
|
-
class GlobalEmoji
|
1849
|
-
include IDObject
|
1850
|
-
|
1851
|
-
# @return [String] the emoji name
|
1852
|
-
attr_reader :name
|
1853
|
-
|
1854
|
-
# @return [Hash<Integer => Array<Role>>] roles this emoji is active for in every server
|
1855
|
-
attr_reader :role_associations
|
1856
|
-
|
1857
|
-
def initialize(data, bot)
|
1858
|
-
@bot = bot
|
1859
|
-
@roles = nil
|
1860
|
-
|
1861
|
-
@name = data.name
|
1862
|
-
@id = data.id
|
1863
|
-
@role_associations = Hash.new([])
|
1864
|
-
@role_associations[data.server.id] = data.roles
|
1865
|
-
end
|
1866
|
-
|
1867
|
-
# @return [String] the layout to mention it (or have it used) in a message
|
1868
|
-
def mention
|
1869
|
-
"<:#{@name}:#{@id}>"
|
1870
|
-
end
|
1871
|
-
|
1872
|
-
alias_method :use, :mention
|
1873
|
-
alias_method :to_s, :mention
|
1874
|
-
|
1875
|
-
# @return [String] the icon URL of the emoji
|
1876
|
-
def icon_url
|
1877
|
-
API.emoji_icon_url(@id)
|
1878
|
-
end
|
1879
|
-
|
1880
|
-
# The inspect method is overwritten to give more useful output
|
1881
|
-
def inspect
|
1882
|
-
"<GlobalEmoji name=#{@name} id=#{@id}>"
|
1883
|
-
end
|
1884
|
-
|
1885
|
-
# @!visibility private
|
1886
|
-
def process_roles(roles)
|
1887
|
-
new_roles = []
|
1888
|
-
return unless roles
|
1889
|
-
roles.each do
|
1890
|
-
role = server.role(role_id)
|
1891
|
-
new_roles << role
|
1892
|
-
end
|
1893
|
-
new_roles
|
1894
|
-
end
|
1895
|
-
end
|
1896
|
-
|
1897
|
-
# Basic attributes a server should have
|
1898
|
-
module ServerAttributes
|
1899
|
-
# @return [String] this server's name.
|
1900
|
-
attr_reader :name
|
1901
|
-
|
1902
|
-
# @return [String] the hexadecimal ID used to identify this server's icon.
|
1903
|
-
attr_reader :icon_id
|
1904
|
-
|
1905
|
-
# Utility function to get the URL for the icon image
|
1906
|
-
# @return [String] the URL to the icon image
|
1907
|
-
def icon_url
|
1908
|
-
return nil unless @icon_id
|
1909
|
-
API.icon_url(@id, @icon_id)
|
1910
|
-
end
|
1911
|
-
end
|
1912
|
-
|
1913
|
-
# Integration Account
|
1914
|
-
class IntegrationAccount
|
1915
|
-
# @return [String] this account's name.
|
1916
|
-
attr_reader :name
|
1917
|
-
|
1918
|
-
# @return [Integer] this account's ID.
|
1919
|
-
attr_reader :id
|
1920
|
-
|
1921
|
-
def initialize(data)
|
1922
|
-
@name = data['name']
|
1923
|
-
@id = data['id'].to_i
|
1924
|
-
end
|
1925
|
-
end
|
1926
|
-
|
1927
|
-
# Server integration
|
1928
|
-
class Integration
|
1929
|
-
include IDObject
|
1930
|
-
|
1931
|
-
# @return [String] the integration name
|
1932
|
-
attr_reader :name
|
1933
|
-
|
1934
|
-
# @return [Server] the server the integration is linked to
|
1935
|
-
attr_reader :server
|
1936
|
-
|
1937
|
-
# @return [User] the user the integration is linked to
|
1938
|
-
attr_reader :user
|
1939
|
-
|
1940
|
-
# @return [Role, nil] the role that this integration uses for "subscribers"
|
1941
|
-
attr_reader :role
|
1942
|
-
|
1943
|
-
# @return [true, false] whether emoticons are enabled
|
1944
|
-
attr_reader :emoticon
|
1945
|
-
alias_method :emoticon?, :emoticon
|
1946
|
-
|
1947
|
-
# @return [String] the integration type (Youtube, Twitch, etc.)
|
1948
|
-
attr_reader :type
|
1949
|
-
|
1950
|
-
# @return [true, false] whether the integration is enabled
|
1951
|
-
attr_reader :enabled
|
1952
|
-
|
1953
|
-
# @return [true, false] whether the integration is syncing
|
1954
|
-
attr_reader :syncing
|
1955
|
-
|
1956
|
-
# @return [IntegrationAccount] the integration account information
|
1957
|
-
attr_reader :account
|
1958
|
-
|
1959
|
-
# @return [Time] the time the integration was synced at
|
1960
|
-
attr_reader :synced_at
|
1961
|
-
|
1962
|
-
# @return [Symbol] the behaviour of expiring subscribers (:remove = Remove User from role; :kick = Kick User from server)
|
1963
|
-
attr_reader :expire_behaviour
|
1964
|
-
alias_method :expire_behavior, :expire_behaviour
|
1965
|
-
|
1966
|
-
# @return [Integer] the grace period before subscribers expire (in days)
|
1967
|
-
attr_reader :expire_grace_period
|
1968
|
-
|
1969
|
-
def initialize(data, bot, server)
|
1970
|
-
@bot = bot
|
1971
|
-
|
1972
|
-
@name = data['name']
|
1973
|
-
@server = server
|
1974
|
-
@id = data['id'].to_i
|
1975
|
-
@enabled = data['enabled']
|
1976
|
-
@syncing = data['syncing']
|
1977
|
-
@type = data['type']
|
1978
|
-
@account = IntegrationAccount.new(data['account'])
|
1979
|
-
@synced_at = Time.parse(data['synced_at'])
|
1980
|
-
@expire_behaviour = [:remove, :kick][data['expire_behavior']]
|
1981
|
-
@expire_grace_period = data['expire_grace_period']
|
1982
|
-
@user = @bot.ensure_user(data['user'])
|
1983
|
-
@role = server.role(data['role_id']) || nil
|
1984
|
-
@emoticon = data['enable_emoticons']
|
1985
|
-
end
|
1986
|
-
|
1987
|
-
# The inspect method is overwritten to give more useful output
|
1988
|
-
def inspect
|
1989
|
-
"<Integration name=#{@name} id=#{@id} type=#{@type} enabled=#{@enabled}>"
|
1990
|
-
end
|
1991
|
-
end
|
1992
|
-
|
1993
|
-
# A server on Discord
|
1994
|
-
class Server
|
1995
|
-
include IDObject
|
1996
|
-
include ServerAttributes
|
1997
|
-
|
1998
|
-
# @return [String] the region the server is on (e. g. `amsterdam`).
|
1999
|
-
attr_reader :region
|
2000
|
-
|
2001
|
-
# @return [Member] The server owner.
|
2002
|
-
attr_reader :owner
|
2003
|
-
|
2004
|
-
# @return [Array<Channel>] an array of all the channels (text and voice) on this server.
|
2005
|
-
attr_reader :channels
|
2006
|
-
|
2007
|
-
# @return [Array<Role>] an array of all the roles created on this server.
|
2008
|
-
attr_reader :roles
|
2009
|
-
|
2010
|
-
# @return [Array<Emoji>] an array of all the emoji available on this server.
|
2011
|
-
attr_reader :emoji
|
2012
|
-
alias_method :emojis, :emoji
|
2013
|
-
|
2014
|
-
# @return [true, false] whether or not this server is large (members > 100). If it is,
|
2015
|
-
# it means the members list may be inaccurate for a couple seconds after starting up the bot.
|
2016
|
-
attr_reader :large
|
2017
|
-
alias_method :large?, :large
|
2018
|
-
|
2019
|
-
# @return [Array<Symbol>] the features of the server (eg. "INVITE_SPLASH")
|
2020
|
-
attr_reader :features
|
2021
|
-
|
2022
|
-
# @return [Integer] the absolute number of members on this server, offline or not.
|
2023
|
-
attr_reader :member_count
|
2024
|
-
|
2025
|
-
# @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').
|
2026
|
-
attr_reader :verification_level
|
2027
|
-
|
2028
|
-
# @return [Integer] the amount of time after which a voice user gets moved into the AFK channel, in seconds.
|
2029
|
-
attr_reader :afk_timeout
|
2030
|
-
|
2031
|
-
# @return [Channel, nil] the AFK voice channel of this server, or nil if none is set
|
2032
|
-
attr_reader :afk_channel
|
2033
|
-
|
2034
|
-
# @return [Hash<Integer => VoiceState>] the hash (user ID => voice state) of voice states of members on this server
|
2035
|
-
attr_reader :voice_states
|
2036
|
-
|
2037
|
-
# @!visibility private
|
2038
|
-
def initialize(data, bot, exists = true)
|
2039
|
-
@bot = bot
|
2040
|
-
@owner_id = data['owner_id'].to_i
|
2041
|
-
@id = data['id'].to_i
|
2042
|
-
update_data(data)
|
2043
|
-
|
2044
|
-
@large = data['large']
|
2045
|
-
@member_count = data['member_count']
|
2046
|
-
@verification_level = [:none, :low, :medium, :high][data['verification_level']]
|
2047
|
-
@splash_id = nil
|
2048
|
-
@embed = nil
|
2049
|
-
@features = data['features'].map { |element| element.downcase.to_sym }
|
2050
|
-
@members = {}
|
2051
|
-
@voice_states = {}
|
2052
|
-
@emoji = {}
|
2053
|
-
|
2054
|
-
process_roles(data['roles'])
|
2055
|
-
process_emoji(data['emojis'])
|
2056
|
-
process_members(data['members'])
|
2057
|
-
process_presences(data['presences'])
|
2058
|
-
process_channels(data['channels'])
|
2059
|
-
process_voice_states(data['voice_states'])
|
2060
|
-
|
2061
|
-
# Whether this server's members have been chunked (resolved using op 8 and GUILD_MEMBERS_CHUNK) yet
|
2062
|
-
@chunked = false
|
2063
|
-
@processed_chunk_members = 0
|
2064
|
-
|
2065
|
-
# Only get the owner of the server actually exists (i. e. not for ServerDeleteEvent)
|
2066
|
-
@owner = member(@owner_id) if exists
|
2067
|
-
end
|
2068
|
-
|
2069
|
-
# @return [Channel] The default channel on this server (usually called #general)
|
2070
|
-
def default_channel
|
2071
|
-
@bot.channel(@id)
|
2072
|
-
end
|
2073
|
-
|
2074
|
-
alias_method :general_channel, :default_channel
|
2075
|
-
|
2076
|
-
# Gets a role on this server based on its ID.
|
2077
|
-
# @param id [Integer] The role ID to look for.
|
2078
|
-
def role(id)
|
2079
|
-
@roles.find { |e| e.id == id }
|
2080
|
-
end
|
2081
|
-
|
2082
|
-
# Gets a member on this server based on user ID
|
2083
|
-
# @param id [Integer] The user ID to look for
|
2084
|
-
# @param request [true, false] Whether the member should be requested from Discord if it's not cached
|
2085
|
-
def member(id, request = true)
|
2086
|
-
id = id.resolve_id
|
2087
|
-
return @members[id] if member_cached?(id)
|
2088
|
-
return nil unless request
|
2089
|
-
|
2090
|
-
member = @bot.member(self, id)
|
2091
|
-
@members[id] = member
|
2092
|
-
rescue
|
2093
|
-
nil
|
2094
|
-
end
|
2095
|
-
|
2096
|
-
# @return [Array<Member>] an array of all the members on this server.
|
2097
|
-
def members
|
2098
|
-
return @members.values if @chunked
|
2099
|
-
|
2100
|
-
@bot.debug("Members for server #{@id} not chunked yet - initiating")
|
2101
|
-
@bot.request_chunks(@id)
|
2102
|
-
sleep 0.05 until @chunked
|
2103
|
-
@members.values
|
2104
|
-
end
|
2105
|
-
|
2106
|
-
alias_method :users, :members
|
2107
|
-
|
2108
|
-
# @return [Array<Integration>] an array of all the integrations connected to this server.
|
2109
|
-
def integrations
|
2110
|
-
integration = JSON.parse(API.server_integrations(@bot.token, @id))
|
2111
|
-
integration.map { |element| Integration.new(element, @bot, self) }
|
2112
|
-
end
|
2113
|
-
|
2114
|
-
# Cache @embed
|
2115
|
-
# @note For internal use only
|
2116
|
-
# @!visibility private
|
2117
|
-
def cache_embed
|
2118
|
-
@embed = JSON.parse(API.server(@bot.token, @id))['embed_enabled'] if @embed.nil?
|
2119
|
-
end
|
2120
|
-
|
2121
|
-
# @return [true, false] whether or not the server has widget enabled
|
2122
|
-
def embed?
|
2123
|
-
cache_embed if @embed.nil?
|
2124
|
-
@embed
|
2125
|
-
end
|
2126
|
-
|
2127
|
-
# @param include_idle [true, false] Whether to count idle members as online.
|
2128
|
-
# @param include_bots [true, false] Whether to include bot accounts in the count.
|
2129
|
-
# @return [Array<Member>] an array of online members on this server.
|
2130
|
-
def online_members(include_idle: false, include_bots: true)
|
2131
|
-
@members.values.select do |e|
|
2132
|
-
((include_idle ? e.idle? : false) || e.online?) && (include_bots ? true : !e.bot_account?)
|
2133
|
-
end
|
2134
|
-
end
|
2135
|
-
|
2136
|
-
alias_method :online_users, :online_members
|
2137
|
-
|
2138
|
-
# @return [Array<Channel>] an array of text channels on this server
|
2139
|
-
def text_channels
|
2140
|
-
@channels.select(&:text?)
|
2141
|
-
end
|
2142
|
-
|
2143
|
-
# @return [Array<Channel>] an array of voice channels on this server
|
2144
|
-
def voice_channels
|
2145
|
-
@channels.select(&:voice?)
|
2146
|
-
end
|
2147
|
-
|
2148
|
-
# @return [String, nil] the widget URL to the server that displays the amount of online members in a
|
2149
|
-
# stylish way. `nil` if the widget is not enabled.
|
2150
|
-
def widget_url
|
2151
|
-
cache_embed if @embed.nil?
|
2152
|
-
return nil unless @embed
|
2153
|
-
API.widget_url(@id)
|
2154
|
-
end
|
2155
|
-
|
2156
|
-
# @param style [Symbol] The style the picture should have. Possible styles are:
|
2157
|
-
# * `: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.
|
2158
|
-
# * `: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.
|
2159
|
-
# * `: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.
|
2160
|
-
# * `: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.
|
2161
|
-
# * `: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.
|
2162
|
-
# @return [String, nil] the widget banner URL to the server that displays the amount of online members,
|
2163
|
-
# server icon and server name in a stylish way. `nil` if the widget is not enabled.
|
2164
|
-
def widget_banner_url(style)
|
2165
|
-
return nil unless @embed
|
2166
|
-
cache_embed if @embed.nil?
|
2167
|
-
API.widget_url(@id, style)
|
2168
|
-
end
|
2169
|
-
|
2170
|
-
# @return [String] the hexadecimal ID used to identify this server's splash image for their VIP invite page.
|
2171
|
-
def splash_id
|
2172
|
-
@splash_id = JSON.parse(API.server(@bot.token, @id))['splash'] if @splash_id.nil?
|
2173
|
-
@splash_id
|
2174
|
-
end
|
2175
|
-
|
2176
|
-
# @return [String, nil] the splash image URL for the server's VIP invite page.
|
2177
|
-
# `nil` if there is no splash image.
|
2178
|
-
def splash_url
|
2179
|
-
splash_id if @splash_id.nil?
|
2180
|
-
return nil unless @splash_id
|
2181
|
-
API.splash_url(@id, @splash_id)
|
2182
|
-
end
|
2183
|
-
|
2184
|
-
# Adds a role to the role cache
|
2185
|
-
# @note For internal use only
|
2186
|
-
# @!visibility private
|
2187
|
-
def add_role(role)
|
2188
|
-
@roles << role
|
2189
|
-
end
|
2190
|
-
|
2191
|
-
# Removes a role from the role cache
|
2192
|
-
# @note For internal use only
|
2193
|
-
# @!visibility private
|
2194
|
-
def delete_role(role_id)
|
2195
|
-
@roles.reject! { |r| r.id == role_id }
|
2196
|
-
@members.each do |_, member|
|
2197
|
-
new_roles = member.roles.reject { |r| r.id == role_id }
|
2198
|
-
member.update_roles(new_roles)
|
2199
|
-
end
|
2200
|
-
@channels.each do |channel|
|
2201
|
-
overwrites = channel.permission_overwrites.reject { |id, _| id == role_id }
|
2202
|
-
channel.update_overwrites(overwrites)
|
2203
|
-
end
|
2204
|
-
end
|
2205
|
-
|
2206
|
-
# Adds a member to the member cache.
|
2207
|
-
# @note For internal use only
|
2208
|
-
# @!visibility private
|
2209
|
-
def add_member(member)
|
2210
|
-
@members[member.id] = member
|
2211
|
-
@member_count += 1
|
2212
|
-
end
|
2213
|
-
|
2214
|
-
# Removes a member from the member cache.
|
2215
|
-
# @note For internal use only
|
2216
|
-
# @!visibility private
|
2217
|
-
def delete_member(user_id)
|
2218
|
-
@members.delete(user_id)
|
2219
|
-
@member_count -= 1
|
2220
|
-
end
|
2221
|
-
|
2222
|
-
# Checks whether a member is cached
|
2223
|
-
# @note For internal use only
|
2224
|
-
# @!visibility private
|
2225
|
-
def member_cached?(user_id)
|
2226
|
-
@members.include?(user_id)
|
2227
|
-
end
|
2228
|
-
|
2229
|
-
# Adds a member to the cache
|
2230
|
-
# @note For internal use only
|
2231
|
-
# @!visibility private
|
2232
|
-
def cache_member(member)
|
2233
|
-
@members[member.id] = member
|
2234
|
-
end
|
2235
|
-
|
2236
|
-
# Updates a member's voice state
|
2237
|
-
# @note For internal use only
|
2238
|
-
# @!visibility private
|
2239
|
-
def update_voice_state(data)
|
2240
|
-
user_id = data['user_id'].to_i
|
2241
|
-
|
2242
|
-
if data['channel_id']
|
2243
|
-
unless @voice_states[user_id]
|
2244
|
-
# Create a new voice state for the user
|
2245
|
-
@voice_states[user_id] = VoiceState.new(user_id)
|
2246
|
-
end
|
2247
|
-
|
2248
|
-
# Update the existing voice state (or the one we just created)
|
2249
|
-
channel = @channels_by_id[data['channel_id'].to_i]
|
2250
|
-
@voice_states[user_id].update(
|
2251
|
-
channel,
|
2252
|
-
data['mute'],
|
2253
|
-
data['deaf'],
|
2254
|
-
data['self_mute'],
|
2255
|
-
data['self_deaf']
|
2256
|
-
)
|
2257
|
-
else
|
2258
|
-
# The user is not in a voice channel anymore, so delete its voice state
|
2259
|
-
@voice_states.delete(user_id)
|
2260
|
-
end
|
2261
|
-
end
|
2262
|
-
|
2263
|
-
# Creates a channel on this server with the given name.
|
2264
|
-
# @param name [String] Name of the channel to create
|
2265
|
-
# @param type [Integer] Type of channel to create (0: text, 2: voice)
|
2266
|
-
# @return [Channel] the created channel.
|
2267
|
-
# @raise [ArgumentError] if type is not 0 or 2
|
2268
|
-
def create_channel(name, type = 0)
|
2269
|
-
raise ArgumentError, 'Channel type must be either 0 (text) or 2 (voice)!' unless [0, 2].include?(type)
|
2270
|
-
response = API::Server.create_channel(@bot.token, @id, name, type)
|
2271
|
-
Channel.new(JSON.parse(response), @bot)
|
2272
|
-
end
|
2273
|
-
|
2274
|
-
# Creates a role on this server which can then be modified. It will be initialized (on Discord's side)
|
2275
|
-
# with the regular role defaults the client uses, i. e. name is "new role", permissions are the default,
|
2276
|
-
# colour is the default etc.
|
2277
|
-
# @return [Role] the created role.
|
2278
|
-
def create_role
|
2279
|
-
response = API::Server.create_role(@bot.token, @id)
|
2280
|
-
role = Role.new(JSON.parse(response), @bot, self)
|
2281
|
-
@roles << role
|
2282
|
-
role
|
2283
|
-
end
|
2284
|
-
|
2285
|
-
# @return [Array<User>] a list of banned users on this server.
|
2286
|
-
def bans
|
2287
|
-
users = JSON.parse(API::Server.bans(@bot.token, @id))
|
2288
|
-
users.map { |e| User.new(e['user'], @bot) }
|
2289
|
-
end
|
2290
|
-
|
2291
|
-
# Bans a user from this server.
|
2292
|
-
# @param user [User, #resolve_id] The user to ban.
|
2293
|
-
# @param message_days [Integer] How many days worth of messages sent by the user should be deleted.
|
2294
|
-
def ban(user, message_days = 0)
|
2295
|
-
API::Server.ban_user(@bot.token, @id, user.resolve_id, message_days)
|
2296
|
-
end
|
2297
|
-
|
2298
|
-
# Unbans a previously banned user from this server.
|
2299
|
-
# @param user [User, #resolve_id] The user to unban.
|
2300
|
-
def unban(user)
|
2301
|
-
API::Server.unban_user(@bot.token, @id, user.resolve_id)
|
2302
|
-
end
|
2303
|
-
|
2304
|
-
# Kicks a user from this server.
|
2305
|
-
# @param user [User, #resolve_id] The user to kick.
|
2306
|
-
def kick(user)
|
2307
|
-
API::Server.remove_member(@bot.token, @id, user.resolve_id)
|
2308
|
-
end
|
2309
|
-
|
2310
|
-
# Forcibly moves a user into a different voice channel. Only works if the bot has the permission needed.
|
2311
|
-
# @param user [User] The user to move.
|
2312
|
-
# @param channel [Channel] The voice channel to move into.
|
2313
|
-
def move(user, channel)
|
2314
|
-
API::Server.update_member(@bot.token, @id, user.id, channel_id: channel.id)
|
2315
|
-
end
|
2316
|
-
|
2317
|
-
# Deletes this server. Be aware that this is permanent and impossible to undo, so be careful!
|
2318
|
-
def delete
|
2319
|
-
API::Server.delete(@bot.token, @id)
|
2320
|
-
end
|
2321
|
-
|
2322
|
-
# Leave the server
|
2323
|
-
def leave
|
2324
|
-
API::User.leave_server(@bot.token, @id)
|
2325
|
-
end
|
2326
|
-
|
2327
|
-
# Transfers server ownership to another user.
|
2328
|
-
# @param user [User] The user who should become the new owner.
|
2329
|
-
def owner=(user)
|
2330
|
-
API::Server.transfer_ownership(@bot.token, @id, user.id)
|
2331
|
-
end
|
2332
|
-
|
2333
|
-
# Sets the server's name.
|
2334
|
-
# @param name [String] The new server name.
|
2335
|
-
def name=(name)
|
2336
|
-
update_server_data(name: name)
|
2337
|
-
end
|
2338
|
-
|
2339
|
-
# Moves the server to another region. This will cause a voice interruption of at most a second.
|
2340
|
-
# @param region [String] The new region the server should be in.
|
2341
|
-
def region=(region)
|
2342
|
-
update_server_data(region: region.to_s)
|
2343
|
-
end
|
2344
|
-
|
2345
|
-
# Sets the server's icon.
|
2346
|
-
# @param icon [String, #read] The new icon, in base64-encoded JPG format.
|
2347
|
-
def icon=(icon)
|
2348
|
-
if icon.respond_to? :read
|
2349
|
-
icon_string = 'data:image/jpg;base64,'
|
2350
|
-
icon_string += Base64.strict_encode64(icon.read)
|
2351
|
-
update_server_data(icon: icon_string)
|
2352
|
-
else
|
2353
|
-
update_server_data(icon: icon)
|
2354
|
-
end
|
2355
|
-
end
|
2356
|
-
|
2357
|
-
# Sets the server's AFK channel.
|
2358
|
-
# @param afk_channel [Channel, nil] The new AFK channel, or `nil` if there should be none set.
|
2359
|
-
def afk_channel=(afk_channel)
|
2360
|
-
update_server_data(afk_channel_id: afk_channel.resolve_id)
|
2361
|
-
end
|
2362
|
-
|
2363
|
-
# Sets the amount of time after which a user gets moved into the AFK channel.
|
2364
|
-
# @param afk_timeout [Integer] The AFK timeout, in seconds.
|
2365
|
-
def afk_timeout=(afk_timeout)
|
2366
|
-
update_server_data(afk_timeout: afk_timeout)
|
2367
|
-
end
|
2368
|
-
|
2369
|
-
# @return [true, false] whether this server has any emoji or not.
|
2370
|
-
def any_emoji?
|
2371
|
-
@emoji.any?
|
2372
|
-
end
|
2373
|
-
|
2374
|
-
alias_method :has_emoji?, :any_emoji?
|
2375
|
-
alias_method :emoji?, :any_emoji?
|
2376
|
-
|
2377
|
-
# Processes a GUILD_MEMBERS_CHUNK packet, specifically the members field
|
2378
|
-
# @note For internal use only
|
2379
|
-
# @!visibility private
|
2380
|
-
def process_chunk(members)
|
2381
|
-
process_members(members)
|
2382
|
-
@processed_chunk_members += members.length
|
2383
|
-
LOGGER.debug("Processed one chunk on server #{@id} - length #{members.length}")
|
2384
|
-
|
2385
|
-
# Don't bother with the rest of the method if it's not truly the last packet
|
2386
|
-
return unless @processed_chunk_members == @member_count
|
2387
|
-
|
2388
|
-
LOGGER.debug("Finished chunking server #{@id}")
|
2389
|
-
|
2390
|
-
# Reset everything to normal
|
2391
|
-
@chunked = true
|
2392
|
-
@processed_chunk_members = 0
|
2393
|
-
end
|
2394
|
-
|
2395
|
-
# Updates the cached data with new data
|
2396
|
-
# @note For internal use only
|
2397
|
-
# @!visibility private
|
2398
|
-
def update_data(new_data)
|
2399
|
-
@name = new_data[:name] || new_data['name'] || @name
|
2400
|
-
@region = new_data[:region] || new_data['region'] || @region
|
2401
|
-
@icon_id = new_data[:icon] || new_data['icon'] || @icon_id
|
2402
|
-
@afk_timeout = new_data[:afk_timeout] || new_data['afk_timeout'].to_i || @afk_timeout
|
2403
|
-
|
2404
|
-
@afk_channel_id = new_data[:afk_channel_id] || new_data['afk_channel_id'].to_i || @afk_channel.id
|
2405
|
-
|
2406
|
-
begin
|
2407
|
-
@afk_channel = @bot.channel(@afk_channel_id, self) if @afk_channel_id.nonzero? && (!@afk_channel || @afk_channel_id != @afk_channel.id)
|
2408
|
-
rescue Discordrb::Errors::NoPermission
|
2409
|
-
LOGGER.debug("AFK channel #{@afk_channel_id} on server #{@id} is unreachable, setting to nil even though one exists")
|
2410
|
-
@afk_channel = nil
|
2411
|
-
end
|
2412
|
-
end
|
2413
|
-
|
2414
|
-
# The inspect method is overwritten to give more useful output
|
2415
|
-
def inspect
|
2416
|
-
"<Server name=#{@name} id=#{@id} large=#{@large} region=#{@region} owner=#{@owner} afk_channel_id=#{@afk_channel_id} afk_timeout=#{@afk_timeout}>"
|
2417
|
-
end
|
2418
|
-
|
2419
|
-
private
|
2420
|
-
|
2421
|
-
def update_server_data(new_data)
|
2422
|
-
API::Server.update(@bot.token, @id,
|
2423
|
-
new_data[:name] || @name,
|
2424
|
-
new_data[:region] || @region,
|
2425
|
-
new_data[:icon_id] || @icon_id,
|
2426
|
-
new_data[:afk_channel_id] || @afk_channel_id,
|
2427
|
-
new_data[:afk_timeout] || @afk_timeout)
|
2428
|
-
update_data(new_data)
|
2429
|
-
end
|
2430
|
-
|
2431
|
-
def process_roles(roles)
|
2432
|
-
# Create roles
|
2433
|
-
@roles = []
|
2434
|
-
@roles_by_id = {}
|
2435
|
-
|
2436
|
-
return unless roles
|
2437
|
-
roles.each do |element|
|
2438
|
-
role = Role.new(element, @bot, self)
|
2439
|
-
@roles << role
|
2440
|
-
@roles_by_id[role.id] = role
|
2441
|
-
end
|
2442
|
-
end
|
2443
|
-
|
2444
|
-
def process_emoji(emoji)
|
2445
|
-
return if emoji.empty?
|
2446
|
-
emoji.each do |element|
|
2447
|
-
new_emoji = Emoji.new(element, @bot, self)
|
2448
|
-
@emoji[new_emoji.id] = new_emoji
|
2449
|
-
end
|
2450
|
-
end
|
2451
|
-
|
2452
|
-
def process_members(members)
|
2453
|
-
return unless members
|
2454
|
-
members.each do |element|
|
2455
|
-
member = Member.new(element, self, @bot)
|
2456
|
-
@members[member.id] = member
|
2457
|
-
end
|
2458
|
-
end
|
2459
|
-
|
2460
|
-
def process_presences(presences)
|
2461
|
-
# Update user statuses with presence info
|
2462
|
-
return unless presences
|
2463
|
-
presences.each do |element|
|
2464
|
-
next unless element['user']
|
2465
|
-
user_id = element['user']['id'].to_i
|
2466
|
-
user = @members[user_id]
|
2467
|
-
if user
|
2468
|
-
user.status = element['status'].to_sym
|
2469
|
-
user.game = element['game'] ? element['game']['name'] : nil
|
2470
|
-
else
|
2471
|
-
LOGGER.warn "Rogue presence update! #{element['user']['id']} on #{@id}"
|
2472
|
-
end
|
2473
|
-
end
|
2474
|
-
end
|
2475
|
-
|
2476
|
-
def process_channels(channels)
|
2477
|
-
@channels = []
|
2478
|
-
@channels_by_id = {}
|
2479
|
-
|
2480
|
-
return unless channels
|
2481
|
-
channels.each do |element|
|
2482
|
-
channel = @bot.ensure_channel(element, self)
|
2483
|
-
@channels << channel
|
2484
|
-
@channels_by_id[channel.id] = channel
|
2485
|
-
end
|
2486
|
-
end
|
2487
|
-
|
2488
|
-
def process_voice_states(voice_states)
|
2489
|
-
return unless voice_states
|
2490
|
-
voice_states.each do |element|
|
2491
|
-
update_voice_state(element)
|
2492
|
-
end
|
2493
|
-
end
|
2494
|
-
end
|
2495
|
-
|
2496
|
-
# A colour (red, green and blue values). Used for role colours. If you prefer the American spelling, the alias
|
2497
|
-
# {ColorRGB} is also available.
|
2498
|
-
class ColourRGB
|
2499
|
-
# @return [Integer] the red part of this colour (0-255).
|
2500
|
-
attr_reader :red
|
2501
|
-
|
2502
|
-
# @return [Integer] the green part of this colour (0-255).
|
2503
|
-
attr_reader :green
|
2504
|
-
|
2505
|
-
# @return [Integer] the blue part of this colour (0-255).
|
2506
|
-
attr_reader :blue
|
2507
|
-
|
2508
|
-
# @return [Integer] the colour's RGB values combined into one integer.
|
2509
|
-
attr_reader :combined
|
2510
|
-
|
2511
|
-
# Make a new colour from the combined value.
|
2512
|
-
# @param combined [Integer] The colour's RGB values combined into one integer
|
2513
|
-
def initialize(combined)
|
2514
|
-
@combined = combined
|
2515
|
-
@red = (combined >> 16) & 0xFF
|
2516
|
-
@green = (combined >> 8) & 0xFF
|
2517
|
-
@blue = combined & 0xFF
|
2518
|
-
end
|
2519
|
-
end
|
2520
|
-
|
2521
|
-
# Alias for the class {ColourRGB}
|
2522
|
-
ColorRGB = ColourRGB
|
2523
|
-
end
|
19
|
+
require 'discordrb/data/activity'
|
20
|
+
require 'discordrb/data/application'
|
21
|
+
require 'discordrb/data/user'
|
22
|
+
require 'discordrb/data/voice_state'
|
23
|
+
require 'discordrb/data/voice_region'
|
24
|
+
require 'discordrb/data/member'
|
25
|
+
require 'discordrb/data/recipient'
|
26
|
+
require 'discordrb/data/profile'
|
27
|
+
require 'discordrb/data/role'
|
28
|
+
require 'discordrb/data/invite'
|
29
|
+
require 'discordrb/data/overwrite'
|
30
|
+
require 'discordrb/data/channel'
|
31
|
+
require 'discordrb/data/embed'
|
32
|
+
require 'discordrb/data/attachment'
|
33
|
+
require 'discordrb/data/message'
|
34
|
+
require 'discordrb/data/reaction'
|
35
|
+
require 'discordrb/data/emoji'
|
36
|
+
require 'discordrb/data/integration'
|
37
|
+
require 'discordrb/data/server'
|
38
|
+
require 'discordrb/data/webhook'
|
39
|
+
require 'discordrb/data/audit_logs'
|