discordrb 3.3.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of discordrb might be problematic. Click here for more details.

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