onyxcord 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. checksums.yaml +7 -0
  2. data/.devcontainer/Dockerfile +13 -0
  3. data/.devcontainer/devcontainer.json +29 -0
  4. data/.devcontainer/postcreate.sh +4 -0
  5. data/.github/CONTRIBUTING.md +13 -0
  6. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  7. data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  8. data/.github/pull_request_template.md +37 -0
  9. data/.github/workflows/ci.yml +78 -0
  10. data/.github/workflows/codeql.yml +65 -0
  11. data/.github/workflows/deploy.yml +54 -0
  12. data/.github/workflows/release.yml +51 -0
  13. data/.gitignore +16 -0
  14. data/.markdownlint.json +4 -0
  15. data/.overcommit.yml +7 -0
  16. data/.rspec +2 -0
  17. data/.rubocop.yml +129 -0
  18. data/.yardopts +1 -0
  19. data/CHANGELOG.md +0 -0
  20. data/Gemfile +7 -0
  21. data/LICENSE.txt +21 -0
  22. data/README.md +305 -0
  23. data/Rakefile +17 -0
  24. data/bin/console +15 -0
  25. data/bin/setup +7 -0
  26. data/lib/onyxcord/allowed_mentions.rb +43 -0
  27. data/lib/onyxcord/api/application.rb +316 -0
  28. data/lib/onyxcord/api/channel.rb +700 -0
  29. data/lib/onyxcord/api/interaction.rb +67 -0
  30. data/lib/onyxcord/api/invite.rb +44 -0
  31. data/lib/onyxcord/api/server.rb +775 -0
  32. data/lib/onyxcord/api/user.rb +158 -0
  33. data/lib/onyxcord/api/webhook.rb +163 -0
  34. data/lib/onyxcord/api.rb +335 -0
  35. data/lib/onyxcord/await.rb +51 -0
  36. data/lib/onyxcord/bot.rb +1971 -0
  37. data/lib/onyxcord/cache.rb +326 -0
  38. data/lib/onyxcord/colour_rgb.rb +43 -0
  39. data/lib/onyxcord/commands/command_bot.rb +511 -0
  40. data/lib/onyxcord/commands/container.rb +112 -0
  41. data/lib/onyxcord/commands/events.rb +11 -0
  42. data/lib/onyxcord/commands/parser.rb +327 -0
  43. data/lib/onyxcord/commands/rate_limiter.rb +144 -0
  44. data/lib/onyxcord/configuration.rb +125 -0
  45. data/lib/onyxcord/container.rb +988 -0
  46. data/lib/onyxcord/data/activity.rb +271 -0
  47. data/lib/onyxcord/data/application.rb +341 -0
  48. data/lib/onyxcord/data/attachment.rb +91 -0
  49. data/lib/onyxcord/data/audit_logs.rb +438 -0
  50. data/lib/onyxcord/data/avatar_decoration.rb +26 -0
  51. data/lib/onyxcord/data/call.rb +22 -0
  52. data/lib/onyxcord/data/channel.rb +1355 -0
  53. data/lib/onyxcord/data/channel_tag.rb +69 -0
  54. data/lib/onyxcord/data/collectibles.rb +47 -0
  55. data/lib/onyxcord/data/component.rb +583 -0
  56. data/lib/onyxcord/data/embed.rb +258 -0
  57. data/lib/onyxcord/data/emoji.rb +123 -0
  58. data/lib/onyxcord/data/install_params.rb +24 -0
  59. data/lib/onyxcord/data/integration.rb +144 -0
  60. data/lib/onyxcord/data/interaction.rb +1141 -0
  61. data/lib/onyxcord/data/invite.rb +137 -0
  62. data/lib/onyxcord/data/member.rb +528 -0
  63. data/lib/onyxcord/data/message.rb +612 -0
  64. data/lib/onyxcord/data/message_activity.rb +41 -0
  65. data/lib/onyxcord/data/overwrite.rb +109 -0
  66. data/lib/onyxcord/data/poll.rb +365 -0
  67. data/lib/onyxcord/data/primary_server.rb +60 -0
  68. data/lib/onyxcord/data/profile.rb +79 -0
  69. data/lib/onyxcord/data/reaction.rb +64 -0
  70. data/lib/onyxcord/data/recipient.rb +34 -0
  71. data/lib/onyxcord/data/role.rb +449 -0
  72. data/lib/onyxcord/data/role_connection_data.rb +69 -0
  73. data/lib/onyxcord/data/role_subscription.rb +41 -0
  74. data/lib/onyxcord/data/scheduled_event.rb +513 -0
  75. data/lib/onyxcord/data/server.rb +1614 -0
  76. data/lib/onyxcord/data/server_preview.rb +68 -0
  77. data/lib/onyxcord/data/snapshot.rb +112 -0
  78. data/lib/onyxcord/data/team.rb +98 -0
  79. data/lib/onyxcord/data/timestamp.rb +69 -0
  80. data/lib/onyxcord/data/user.rb +324 -0
  81. data/lib/onyxcord/data/voice_region.rb +46 -0
  82. data/lib/onyxcord/data/voice_state.rb +41 -0
  83. data/lib/onyxcord/data/webhook.rb +238 -0
  84. data/lib/onyxcord/data.rb +57 -0
  85. data/lib/onyxcord/errors.rb +246 -0
  86. data/lib/onyxcord/event_executor.rb +80 -0
  87. data/lib/onyxcord/events/await.rb +48 -0
  88. data/lib/onyxcord/events/bans.rb +60 -0
  89. data/lib/onyxcord/events/channels.rb +225 -0
  90. data/lib/onyxcord/events/generic.rb +129 -0
  91. data/lib/onyxcord/events/guilds.rb +269 -0
  92. data/lib/onyxcord/events/integrations.rb +100 -0
  93. data/lib/onyxcord/events/interactions.rb +624 -0
  94. data/lib/onyxcord/events/invites.rb +127 -0
  95. data/lib/onyxcord/events/lifetime.rb +31 -0
  96. data/lib/onyxcord/events/members.rb +110 -0
  97. data/lib/onyxcord/events/message.rb +399 -0
  98. data/lib/onyxcord/events/polls.rb +118 -0
  99. data/lib/onyxcord/events/presence.rb +131 -0
  100. data/lib/onyxcord/events/raw.rb +74 -0
  101. data/lib/onyxcord/events/reactions.rb +218 -0
  102. data/lib/onyxcord/events/roles.rb +87 -0
  103. data/lib/onyxcord/events/scheduled_events.rb +171 -0
  104. data/lib/onyxcord/events/threads.rb +100 -0
  105. data/lib/onyxcord/events/typing.rb +73 -0
  106. data/lib/onyxcord/events/voice_server_update.rb +48 -0
  107. data/lib/onyxcord/events/voice_state_update.rb +106 -0
  108. data/lib/onyxcord/events/webhooks.rb +65 -0
  109. data/lib/onyxcord/gateway.rb +890 -0
  110. data/lib/onyxcord/id_object.rb +39 -0
  111. data/lib/onyxcord/light/data.rb +62 -0
  112. data/lib/onyxcord/light/integrations.rb +73 -0
  113. data/lib/onyxcord/light/light_bot.rb +58 -0
  114. data/lib/onyxcord/light.rb +8 -0
  115. data/lib/onyxcord/logger.rb +120 -0
  116. data/lib/onyxcord/message_components.rb +70 -0
  117. data/lib/onyxcord/paginator.rb +60 -0
  118. data/lib/onyxcord/permissions.rb +255 -0
  119. data/lib/onyxcord/rate_limiter/gateway.rb +42 -0
  120. data/lib/onyxcord/rate_limiter/rest.rb +89 -0
  121. data/lib/onyxcord/version.rb +7 -0
  122. data/lib/onyxcord/voice/encoder.rb +115 -0
  123. data/lib/onyxcord/voice/network.rb +380 -0
  124. data/lib/onyxcord/voice/opcodes.rb +29 -0
  125. data/lib/onyxcord/voice/sodium.rb +157 -0
  126. data/lib/onyxcord/voice/timer.rb +19 -0
  127. data/lib/onyxcord/voice/voice_bot.rb +386 -0
  128. data/lib/onyxcord/webhooks.rb +14 -0
  129. data/lib/onyxcord/websocket.rb +62 -0
  130. data/lib/onyxcord.rb +180 -0
  131. data/onyxcord-webhooks.gemspec +30 -0
  132. data/onyxcord.gemspec +50 -0
  133. metadata +421 -0
@@ -0,0 +1,1355 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'onyxcord/webhooks/view'
4
+ require 'onyxcord/message_components'
5
+ require 'time'
6
+
7
+ module OnyxCord
8
+ # A Discord channel, including data like the topic
9
+ class Channel
10
+ include IDObject
11
+
12
+ # Map of channel types.
13
+ TYPES = {
14
+ text: 0,
15
+ dm: 1,
16
+ voice: 2,
17
+ group: 3,
18
+ category: 4,
19
+ news: 5,
20
+ store: 6,
21
+ news_thread: 10,
22
+ public_thread: 11,
23
+ private_thread: 12,
24
+ stage_voice: 13,
25
+ directory: 14,
26
+ forum: 15,
27
+ media: 16
28
+ }.freeze
29
+
30
+ # Map of channel flags.
31
+ FLAGS = {
32
+ pinned: 1 << 1,
33
+ require_tag: 1 << 4,
34
+ hide_download_options: 1 << 15
35
+ }.freeze
36
+
37
+ # Map of forum layouts.
38
+ FORUM_LAYOUTS = {
39
+ not_set: 0,
40
+ list_view: 1,
41
+ gallery_view: 2
42
+ }.freeze
43
+
44
+ # Map of forum sort orders.
45
+ FORUM_SORT_ORDERS = {
46
+ latest_activity: 0,
47
+ creation_time: 1
48
+ }.freeze
49
+
50
+ # Map of video quality modes.
51
+ VIDEO_QUALITY_MODES = {
52
+ auto: 1,
53
+ full: 2
54
+ }.freeze
55
+
56
+ # @return [String] this channel's name.
57
+ attr_reader :name
58
+
59
+ # @return [Integer, nil] the ID of the parent channel, if this channel is inside a category. If this channel is a
60
+ # thread, this is the text channel it is a child to.
61
+ attr_reader :parent_id
62
+
63
+ # @return [Integer] the type of this channel
64
+ # @see TYPES
65
+ attr_reader :type
66
+
67
+ # @return [Integer, nil] the ID of the owner of the group channel or nil if this is not a group channel. If this
68
+ # channel is a thread, this is the member that started the thread.
69
+ attr_reader :owner_id
70
+
71
+ # @return [Array<Recipient>, nil] the array of recipients of the private messages, or nil if this is not a Private channel
72
+ attr_reader :recipients
73
+
74
+ # @return [String] the channel's topic
75
+ attr_reader :topic
76
+
77
+ # @return [Integer] the bitrate (in bps) of the channel
78
+ attr_reader :bitrate
79
+
80
+ # @return [Integer] the amount of users that can be in the channel. `0` means it is unlimited.
81
+ attr_reader :user_limit
82
+ alias_method :limit, :user_limit
83
+
84
+ # @return [Integer] the channel's position on the channel list
85
+ attr_reader :position
86
+
87
+ # @return [Integer] the amount of time (in seconds) users need to wait to send in between messages.
88
+ attr_reader :rate_limit_per_user
89
+ alias_method :slowmode_rate, :rate_limit_per_user
90
+
91
+ # @return [Integer, nil] An approximate count of messages sent in a thread, excluding deleted messages.
92
+ attr_reader :message_count
93
+
94
+ # @return [Integer, nil] An approximate count of members in a thread. Stops counting at 50.
95
+ attr_reader :member_count
96
+
97
+ # @return [true, false, nil] Whether or not this thread is archived.
98
+ attr_reader :archived
99
+ alias_method :archived?, :archived
100
+
101
+ # @return [Integer, nil] How long after the last message before a thread is automatically archived.
102
+ attr_reader :auto_archive_duration
103
+
104
+ # @return [Time, nil] The timestamp of when this threads status last changed.
105
+ attr_reader :archive_timestamp
106
+
107
+ # @return [true, false, nil] Whether this thread is locked or not.
108
+ attr_reader :locked
109
+ alias_method :locked?, :locked
110
+
111
+ # @return [Time, nil] When the current user joined this thread.
112
+ attr_reader :join_timestamp
113
+
114
+ # @return [Integer, nil] Member flags for this thread, used for notifications.
115
+ attr_reader :member_flags
116
+
117
+ # @return [true, false, nil] For private threads, determines whether non-moderators can add other non-moderators to
118
+ # a thread.
119
+ attr_reader :invitable
120
+ alias_method :invitable?, :invitable
121
+
122
+ # @return [Time, nil] The time at when the last pinned message was pinned in this channel.
123
+ attr_reader :last_pin_timestamp
124
+
125
+ # @return [Integer, nil] The ID of the last message sent in this channel. This may not point to a valid message.
126
+ attr_reader :last_message_id
127
+
128
+ # @return [Integer] An approximate count of messages sent in this thread, including deleted messages.
129
+ attr_reader :total_message_sent
130
+ alias_method :total_messages_sent, :total_message_sent
131
+
132
+ # @return [Integer] The flags set on this channel combined as a bitfield.
133
+ attr_reader :flags
134
+
135
+ # @return [String, nil] The ID of the RTC voice region for this voice or stage channel. A region of `nil` means the
136
+ # the voice region will automatically be determined by Discord.
137
+ attr_reader :voice_region
138
+
139
+ # @return [Integer, nil] The video quality mode of this voice or stage channel.
140
+ attr_reader :video_quality_mode
141
+
142
+ # @return [Integer, nil] The default client-side duration before a thread is automatically hidden.
143
+ attr_reader :default_auto_archive_duration
144
+
145
+ # @return [Integer, nil] The default sorting order for threads in this forum or media channel.
146
+ attr_reader :default_sort_order
147
+
148
+ # @return [Integer, nil] The default layout used to display threads in this forum or media channel.
149
+ attr_reader :default_forum_layout
150
+
151
+ # @return [Integer, nil] The initial slowmode rate set on newly created threads in this channel.
152
+ attr_reader :default_thread_rate_limit_per_user
153
+
154
+ # @return [true, false] whether or not this channel is a PM or group channel.
155
+ def private?
156
+ pm? || group?
157
+ end
158
+
159
+ # @return [String] a string that will mention the channel as a clickable link on Discord.
160
+ def mention
161
+ "<##{@id}>"
162
+ end
163
+
164
+ # @return [Recipient, nil] the recipient of the private messages, or nil if this is not a PM channel
165
+ def recipient
166
+ @recipients.first if pm?
167
+ end
168
+
169
+ # @!visibility private
170
+ def initialize(data, bot, server = nil)
171
+ @bot = bot
172
+ # data is sometimes a Hash and other times an array of Hashes, you only want the last one if it's an array
173
+ data = data[-1] if data.is_a?(Array)
174
+
175
+ @id = data['id'].to_i
176
+ @type = data['type'] || 0
177
+ @topic = data['topic']
178
+ @bitrate = data['bitrate']
179
+ @user_limit = data['user_limit']
180
+ @position = data['position']
181
+ @parent_id = data['parent_id']&.to_i
182
+
183
+ if private?
184
+ @recipients = []
185
+ data['recipients']&.each do |recipient|
186
+ recipient_user = bot.ensure_user(recipient)
187
+ @recipients << Recipient.new(recipient_user, self, bot)
188
+ end
189
+ if pm?
190
+ @name = @recipients.first.username
191
+ else
192
+ @name = data['name']
193
+ @owner_id = data['owner_id']
194
+ end
195
+ else
196
+ @name = data['name']
197
+ @server_id = server&.id || data['guild_id'].to_i
198
+ @server = server
199
+ end
200
+
201
+ @nsfw = data['nsfw'] || false
202
+ @rate_limit_per_user = data['rate_limit_per_user'] || 0
203
+ @message_count = data['message_count']
204
+ @member_count = data['member_count']
205
+ @total_message_sent = data['total_message_sent'] || 0
206
+
207
+ if (metadata = data['thread_metadata'])
208
+ @archived = metadata['archived']
209
+ @auto_archive_duration = metadata['auto_archive_duration']
210
+ @archive_timestamp = Time.iso8601(metadata['archive_timestamp'])
211
+ @locked = metadata['locked']
212
+ @invitable = metadata['invitable']
213
+ @create_timestamp = Time.iso8601(metadata['create_timestamp']) if metadata['create_timestamp']
214
+ end
215
+
216
+ if (member = data['member'])
217
+ @join_timestamp = Time.iso8601(member['join_timestamp'])
218
+ @member_flags = member['flags']
219
+ end
220
+
221
+ @flags = data['flags'] || 0
222
+ @voice_region = data['rtc_region']
223
+ @video_quality_mode = data['video_quality_mode']
224
+ @last_message_id = data['last_message_id']&.to_i
225
+
226
+ @default_auto_archive_duration = data['default_auto_archive_duration']
227
+ @default_sort_order = data['default_sort_order']
228
+ @default_forum_layout = data['default_forum_layout']
229
+ @default_thread_rate_limit_per_user = data['default_thread_rate_limit_per_user']
230
+
231
+ @applied_tags = data['applied_tags']&.map(&:to_i) || []
232
+
233
+ process_available_tags(data['available_tags'])
234
+ process_last_pin_timestamp(data['last_pin_timestamp'])
235
+ process_permission_overwrites(data['permission_overwrites'])
236
+ process_default_reaction_emoji(data['default_reaction_emoji'])
237
+ end
238
+
239
+ # @return [Server, nil] the server this channel is on. If this channel is a PM channel, it will be nil.
240
+ # @raise [OnyxCord::Errors::NoPermission] This can happen when receiving interactions for servers in which the bot is not
241
+ # authorized with the `bot` scope.
242
+ def server
243
+ return @server if @server
244
+ return nil if private?
245
+
246
+ @server = @bot.server(@server_id)
247
+ raise OnyxCord::Errors::NoPermission, 'The bot does not have access to this server' unless @server
248
+
249
+ @server
250
+ end
251
+
252
+ # @return [true, false] whether or not this channel is a text channel
253
+ def text?
254
+ @type.zero?
255
+ end
256
+
257
+ # @return [true, false] whether or not this channel is a PM channel.
258
+ def pm?
259
+ @type == 1
260
+ end
261
+
262
+ # @return [true, false] whether or not this channel is a voice channel.
263
+ def voice?
264
+ @type == 2
265
+ end
266
+
267
+ # @return [true, false] whether or not this channel is a group channel.
268
+ def group?
269
+ @type == 3
270
+ end
271
+
272
+ # @return [true, false] whether or not this channel is a category channel.
273
+ def category?
274
+ @type == 4
275
+ end
276
+
277
+ # @return [true, false] whether or not this channel is a news channel.
278
+ def news?
279
+ @type == 5
280
+ end
281
+
282
+ # @return [true, false] whether or not this channel is a store channel.
283
+ def store?
284
+ @type == 6
285
+ end
286
+
287
+ # @return [true, false] whether or not this channel is a news thread.
288
+ def news_thread?
289
+ @type == 10
290
+ end
291
+
292
+ # @return [true, false] whether or not this channel is a public thread.
293
+ def public_thread?
294
+ @type == 11
295
+ end
296
+
297
+ # @return [true, false] whether or not this channel is a private thread.
298
+ def private_thread?
299
+ @type == 12
300
+ end
301
+
302
+ # @return [true, false] whether or not this channel is a thread.
303
+ def thread?
304
+ news_thread? || public_thread? || private_thread?
305
+ end
306
+
307
+ # @return [true, false] whether or not this channel is a stage channel.
308
+ def stage?
309
+ @type == 13
310
+ end
311
+
312
+ # @return [true, false] whether or not this channel is a directory channel.
313
+ def directory?
314
+ @type == 14
315
+ end
316
+
317
+ # @return [true, false] whether or not this channel is a forum channel.
318
+ def forum?
319
+ @type == 15
320
+ end
321
+
322
+ # @return [true, false] whether or not this channel is a media channel.
323
+ def media?
324
+ @type == 16
325
+ end
326
+
327
+ # Check if this channel is a forum or media channel.
328
+ # @return [true, false] whether or not only threads can be created in this channel.
329
+ def thread_only?
330
+ forum? || media?
331
+ end
332
+
333
+ # @return [Channel, nil] the category channel, if this channel is in a category
334
+ def category
335
+ @bot.channel(@parent_id) if @parent_id
336
+ end
337
+
338
+ alias_method :parent, :category
339
+
340
+ # Sets this channels parent category
341
+ # @param channel [Channel, String, Integer] the target category channel, or its ID
342
+ # @raise [ArgumentError] if the target channel isn't a category
343
+ def category=(channel)
344
+ channel = @bot.channel(channel)
345
+ raise ArgumentError, 'Cannot set parent category to a channel that isn\'t a category' unless channel.category?
346
+
347
+ modify(parent: channel)
348
+ end
349
+
350
+ alias_method :parent=, :category=
351
+
352
+ # Sorts this channel's position to follow another channel.
353
+ # @param other [Channel, String, Integer, nil] The channel, or its ID, below which this channel should be sorted. If the given
354
+ # channel is a category, this channel will be sorted at the top of that category. If it is `nil`, the channel will
355
+ # be sorted at the top of the channel list.
356
+ # @param lock_permissions [true, false] Whether the channel's permissions should be synced to the category's
357
+ def sort_after(other = nil, lock_permissions = false)
358
+ raise TypeError, 'other must be one of Channel, NilClass, String, or Integer' unless other.is_a?(Channel) || other.nil? || other.respond_to?(:resolve_id)
359
+
360
+ other = @bot.channel(other.resolve_id) if other
361
+
362
+ # Container for the API request payload
363
+ move_argument = []
364
+
365
+ if other
366
+ raise ArgumentError, 'Can only sort a channel after a channel of the same type!' unless other.category? || (@type == other.type)
367
+
368
+ raise ArgumentError, 'Can only sort a channel after a channel in the same server!' unless other.server == server
369
+
370
+ # Store `others` parent (or if `other` is a category itself)
371
+ parent = if category? && other.category?
372
+ # If we're sorting two categories, there is no new parent
373
+ nil
374
+ elsif other.category?
375
+ # `other` is the category this channel will be moved into
376
+ other
377
+ else
378
+ # `other`'s parent is the category this channel will be
379
+ # moved into (if it exists)
380
+ other.parent
381
+ end
382
+ end
383
+
384
+ # Collect and sort the IDs within the context (category or not) that we
385
+ # need to form our payload with
386
+ ids = if parent
387
+ parent.children
388
+ else
389
+ server.channels.reject(&:parent_id).select { |c| c.type == @type }
390
+ end.sort_by(&:position).map(&:id)
391
+
392
+ # Move our channel ID after the target ID by deleting it,
393
+ # getting the index of `other`, and inserting it after.
394
+ ids.delete(@id) if ids.include?(@id)
395
+ index = other ? (ids.index { |c| c == other.id } || -1) + 1 : 0
396
+ ids.insert(index, @id)
397
+
398
+ # Generate `move_argument`, making the positions in order from how
399
+ # we have sorted them in the above logic
400
+ ids.each_with_index do |id, pos|
401
+ # These keys are present in each element
402
+ hash = { id: id, position: pos }
403
+
404
+ # Conditionally add `lock_permissions` and `parent_id` if we're
405
+ # iterating past ourselves
406
+ if id == @id
407
+ hash[:lock_permissions] = true if lock_permissions
408
+ hash[:parent_id] = parent.nil? ? nil : parent.id
409
+ end
410
+
411
+ # Add it to the stack
412
+ move_argument << hash
413
+ end
414
+
415
+ API::Server.update_channel_positions(@bot.token, @server_id, move_argument)
416
+ end
417
+
418
+ # Check if this channel is marked as NSFW.
419
+ # @return [true, false] Whether or not this channel is marked as NSFW.
420
+ def nsfw?
421
+ thread? ? parent.nsfw? : @nsfw
422
+ end
423
+
424
+ # Get the time at when this channel was created at.
425
+ # @return [Time, nil] The time at when the channel was created at.
426
+ def creation_time
427
+ return @create_timestamp if @create_timestamp
428
+
429
+ Time.at(((@id >> 22) + OnyxCord::DISCORD_EPOCH) / 1000.0)
430
+ end
431
+
432
+ # Sets whether this channel is NSFW
433
+ # @param nsfw [true, false]
434
+ # @raise [ArgumentError] if value isn't one of true, false
435
+ def nsfw=(nsfw)
436
+ raise ArgumentError, 'nsfw value must be true or false' unless nsfw.is_a?(TrueClass) || nsfw.is_a?(FalseClass)
437
+
438
+ modify(nsfw: nsfw)
439
+ end
440
+
441
+ # This channel's permission overwrites
442
+ # @overload permission_overwrites
443
+ # The overwrites represented as a hash of role/user ID
444
+ # to an Overwrite object
445
+ # @return [Hash<Integer => Overwrite>] the channel's permission overwrites
446
+ # @overload permission_overwrites(type)
447
+ # Return an array of a certain type of overwrite
448
+ # @param type [Symbol] the kind of overwrite to return
449
+ # @return [Array<Overwrite>]
450
+ def permission_overwrites(type = nil)
451
+ return @permission_overwrites unless type
452
+
453
+ @permission_overwrites.values.select { |e| e.type == type }
454
+ end
455
+
456
+ alias_method :overwrites, :permission_overwrites
457
+
458
+ # Bulk sets this channels permission overwrites
459
+ # @param overwrites [Array<Overwrite>]
460
+ def permission_overwrites=(overwrites)
461
+ modify(permission_overwrites: overwrites)
462
+ end
463
+
464
+ # Sets the amount of time (in seconds) users have to wait in between sending messages.
465
+ # @param rate [Integer]
466
+ # @raise [ArgumentError] if value isn't between 0 and 21600
467
+ def rate_limit_per_user=(rate)
468
+ raise ArgumentError, 'rate_limit_per_user must be between 0 and 21600' unless rate.between?(0, 21_600)
469
+
470
+ modify(rate_limit_per_user: rate)
471
+ end
472
+
473
+ alias_method :slowmode_rate=, :rate_limit_per_user=
474
+
475
+ # Syncs this channels overwrites with its parent category
476
+ # @raise [RuntimeError] if this channel is not in a category
477
+ def sync_overwrites
478
+ raise 'Cannot sync overwrites on a channel with no parent category' unless parent
479
+
480
+ self.permission_overwrites = parent.permission_overwrites
481
+ end
482
+
483
+ alias_method :sync, :sync_overwrites
484
+
485
+ # @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
486
+ def synchronized?
487
+ return unless parent
488
+
489
+ permission_overwrites == parent.permission_overwrites
490
+ end
491
+
492
+ alias_method :synced?, :synchronized?
493
+
494
+ # Returns the children of this channel, if it is a category. Otherwise returns an empty array.
495
+ # @return [Array<Channel>]
496
+ def children
497
+ return [] unless category?
498
+
499
+ server.channels.select { |c| c.parent_id == id }
500
+ end
501
+
502
+ alias_method :channels, :children
503
+
504
+ # Returns the text channels in this category, if it is a category channel. Otherwise returns an empty array.
505
+ # @return [Array<Channel>]
506
+ def text_channels
507
+ children.select(&:text?)
508
+ end
509
+
510
+ # Returns the voice channels in this category, if it is a category channel. Otherwise returns an empty array.
511
+ # @return [Array<Channel>]
512
+ def voice_channels
513
+ children.select(&:voice?)
514
+ end
515
+
516
+ # @return [Overwrite] any member-type permission overwrites on this channel
517
+ def member_overwrites
518
+ permission_overwrites :member
519
+ end
520
+
521
+ # @return [Overwrite] any role-type permission overwrites on this channel
522
+ def role_overwrites
523
+ permission_overwrites :role
524
+ end
525
+
526
+ # @return [true, false] whether or not this channel is the default channel
527
+ def default_channel?
528
+ server.default_channel == self
529
+ end
530
+
531
+ alias_method :default?, :default_channel?
532
+
533
+ # @return [true, false] whether or not this channel has slowmode enabled
534
+ def slowmode?
535
+ @rate_limit_per_user != 0
536
+ end
537
+
538
+ # Sends a message to this channel.
539
+ # @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
540
+ # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
541
+ # @param embed [Hash, OnyxCord::Webhooks::Embed, nil] The rich embed to append to this message.
542
+ # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
543
+ # @param allowed_mentions [Hash, OnyxCord::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
544
+ # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
545
+ # @param components [View, Array<Hash>] Interaction components to associate with this message.
546
+ # @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2), SUPPRESS_NOTIFICATIONS (1 << 12), and IS_COMPONENTS_V2 (1 << 15) can be set.
547
+ # @return [Message] the message that was sent.
548
+ def send_message(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0)
549
+ @bot.send_message(@id, content, tts, embed, attachments, allowed_mentions, message_reference, components, flags)
550
+ end
551
+
552
+ alias_method :send, :send_message
553
+
554
+ # Sends a temporary message to this channel.
555
+ # @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
556
+ # @param timeout [Float] The amount of time in seconds after which the message sent will be deleted.
557
+ # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
558
+ # @param embed [Hash, OnyxCord::Webhooks::Embed, nil] The rich embed to append to this message.
559
+ # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
560
+ # @param allowed_mentions [Hash, OnyxCord::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
561
+ # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
562
+ # @param components [View, Array<Hash>] Interaction components to associate with this message.
563
+ # @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2), SUPPRESS_NOTIFICATIONS (1 << 12), and IS_COMPONENTS_V2 (1 << 15) can be set.
564
+ def send_temporary_message(content, timeout, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0)
565
+ @bot.send_temporary_message(@id, content, timeout, tts, embed, attachments, allowed_mentions, message_reference, components, flags)
566
+ end
567
+
568
+ # Convenience method to send a message with an embed.
569
+ # @example Send a message with an embed
570
+ # channel.send_embed do |embed|
571
+ # embed.title = 'The Ruby logo'
572
+ # embed.image = OnyxCord::Webhooks::EmbedImage.new(url: 'https://www.ruby-lang.org/images/header-ruby-logo.png')
573
+ # end
574
+ # @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.
575
+ # @param embed [OnyxCord::Webhooks::Embed, nil] The embed to start the building process with, or nil if one should be created anew.
576
+ # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
577
+ # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
578
+ # @param allowed_mentions [Hash, OnyxCord::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
579
+ # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
580
+ # @param components [View, Array<Hash>] Interaction components to associate with this message.
581
+ # @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2), SUPPRESS_NOTIFICATIONS (1 << 12), and IS_COMPONENTS_V2 (1 << 15) can be set.
582
+ # @yield [embed] Yields the embed to allow for easy building inside a block.
583
+ # @yieldparam embed [OnyxCord::Webhooks::Embed] The embed from the parameters, or a new one.
584
+ # @return [Message] The resulting message.
585
+ def send_embed(message = '', embed = nil, attachments = nil, tts = false, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0)
586
+ embed ||= OnyxCord::Webhooks::Embed.new
587
+ view = OnyxCord::Webhooks::View.new
588
+
589
+ yield(embed, view) if block_given?
590
+
591
+ send_message(message, tts, embed, attachments, allowed_mentions, message_reference, components || view.to_a, flags)
592
+ end
593
+
594
+ # Send a message to this channel.
595
+ # @example This sends a silent message with an embed.
596
+ # channel.send_message!(content: 'Hi <@171764626755813376>', flags: :suppress_notifications) do |builder|
597
+ # builder.add_embed do |embed|
598
+ # embed.title = 'The Ruby logo'
599
+ # embed.image = OnyxCord::Webhooks::EmbedImage.new(url: 'https://www.ruby-lang.org/images/header-ruby-logo.png')
600
+ # end
601
+ # end
602
+ # @param content [String] The content of the message. Should not be longer than 2000 characters or it will result in an error.
603
+ # @param timeout [Float, nil] The amount of time in seconds after which the message sent will be deleted, or `nil` if the message should not be deleted.
604
+ # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
605
+ # @param embeds [Array<Hash, Webhooks::Embed>] The embeds that should be attached to the message.
606
+ # @param attachments [Array<File>] Files that can be referenced in embeds and components via `attachment://file.png`.
607
+ # @param allowed_mentions [Hash, OnyxCord::AllowedMentions, nil] Mentions that are allowed to ping on this message.
608
+ # @param reference [Message, String, Integer, Hash, nil] The optional message, or message ID, to reply to or forward.
609
+ # @param components [View, Array<#to_h>] Interaction components to associate with this message.
610
+ # @param flags [Integer, Symbol, Array<Symbol, Integer>] Flags for this message. Currently only `:suppress_embeds` (1 << 2), `:suppress_notifications` (1 << 12), and `:uikit_components` (1 << 15) can be set.
611
+ # @param has_components [true, false] Whether this message includes any V2 components. Enabling this disables sending content, polls, and embeds.
612
+ # @param nonce [nil, String, Integer, false] The 25 character nonce that should be used when sending this message.
613
+ # @param enforce_nonce [true, false] Whether the provided nonce should be enforced and used for message de-duplication.
614
+ # @param poll [Hash, Poll::Builder, Poll, nil] The poll that should be attached to the message.
615
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the builder overwrite method data.
616
+ # @yieldparam view [Webhooks::View] An optional component builder. Arguments passed to the builder overwrite method data.
617
+ # @return [Message, nil] The resulting message that was created, or `nil` if the `timeout` parameter was set to a non `nil` value.
618
+ def send_message!(content: '', timeout: nil, tts: false, embeds: [], attachments: nil, allowed_mentions: nil, reference: nil, components: nil, flags: 0, has_components: false, components_v2: false, nonce: nil, enforce_nonce: false, poll: nil)
619
+ builder = OnyxCord::Webhooks::Builder.new
620
+ view = OnyxCord::Webhooks::View.new
621
+
622
+ builder.tts = tts
623
+ builder.poll = poll
624
+ builder.content = content
625
+ embeds&.each { |embed| builder << embed }
626
+ builder.allowed_mentions = allowed_mentions
627
+
628
+ yield(builder, view) if block_given?
629
+
630
+ components = components&.to_a || view.to_a
631
+ flags = Array(flags).map { |flag| OnyxCord::Message::FLAGS[flag] || flag }.reduce(0, &:|)
632
+ flags = OnyxCord::MessageComponents.apply_v2_flag(flags, components, force: has_components || components_v2)
633
+ builder = builder.to_json_hash
634
+
635
+ if timeout
636
+ @bot.send_temporary_message(@id, builder[:content], timeout, builder[:tts], builder[:embeds], attachments, builder[:allowed_mentions], reference, components, flags, nonce, enforce_nonce, builder[:poll])
637
+ else
638
+ @bot.send_message(@id, builder[:content], builder[:tts], builder[:embeds], attachments, builder[:allowed_mentions], reference, components, flags, nonce, enforce_nonce, builder[:poll])
639
+ end
640
+ end
641
+
642
+ # Sends multiple messages to a channel
643
+ # @param content [Array<String>] The messages to send.
644
+ def send_multiple(content)
645
+ content.each { |text| send_message!(content: text) }
646
+ end
647
+
648
+ # Splits a message into chunks whose length is at most the Discord character limit, then sends them individually.
649
+ # Useful for sending long messages, but be wary of rate limits!
650
+ def split_send(content)
651
+ send_multiple(OnyxCord.split_message(content))
652
+ nil
653
+ end
654
+
655
+ # Sends a file to this channel. If it is an image, it will be embedded.
656
+ # @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)
657
+ # @param caption [string] The caption for the file.
658
+ # @param tts [true, false] Whether or not this file's caption should be sent using Discord text-to-speech.
659
+ # @param filename [String] Overrides the filename of the uploaded file
660
+ # @param spoiler [true, false] Whether or not this file should appear as a spoiler.
661
+ # @example Send a file from disk
662
+ # channel.send_file(File.open('rubytaco.png', 'r'))
663
+ def send_file(file, caption: nil, tts: false, filename: nil, spoiler: nil)
664
+ @bot.send_file(@id, file, caption: caption, tts: tts, filename: filename, spoiler: spoiler)
665
+ end
666
+
667
+ # Deletes a message on this channel. Mostly useful in case a message needs to be deleted when only the ID is known
668
+ # @param message [Message, String, Integer, String, Integer] The message, or its ID, that should be deleted.
669
+ def delete_message(message)
670
+ API::Channel.delete_message(@bot.token, @id, message.resolve_id)
671
+ end
672
+
673
+ # Permanently deletes this channel
674
+ # @param reason [String] The reason the for the channel deletion.
675
+ def delete(reason = nil)
676
+ API::Channel.delete(@bot.token, @id, reason)
677
+ end
678
+
679
+ # Sets this channel's name. The name must be alphanumeric with dashes, unless this is a voice channel (then there are no limitations)
680
+ # @param name [String] The new name.
681
+ def name=(name)
682
+ modify(name: name)
683
+ end
684
+
685
+ # Sets this channel's topic.
686
+ # @param topic [String] The new topic.
687
+ def topic=(topic)
688
+ raise 'Tried to set topic on voice channel' if voice?
689
+
690
+ modify(topic: topic)
691
+ end
692
+
693
+ # Sets this channel's bitrate.
694
+ # @param bitrate [Integer] The new bitrate (in bps). Number has to be between 8000-96000 (128000 for VIP servers)
695
+ def bitrate=(bitrate)
696
+ raise 'Tried to set bitrate on text channel' if text?
697
+
698
+ modify(bitrate: bitrate)
699
+ end
700
+
701
+ # Sets this channel's user limit.
702
+ # @param limit [Integer] The new user limit. `0` for unlimited, has to be a number between 0-99
703
+ def user_limit=(limit)
704
+ raise 'Tried to set user_limit on text channel' if text?
705
+
706
+ modify(user_limit: limit)
707
+ end
708
+
709
+ alias_method :limit=, :user_limit=
710
+
711
+ # Sets this channel's position in the list.
712
+ # @param position [Integer] The new position.
713
+ def position=(position)
714
+ modify(position: position)
715
+ end
716
+
717
+ # Defines a permission overwrite for this channel that sets the specified thing to the specified allow and deny
718
+ # permission sets, or change an existing one.
719
+ # @overload define_overwrite(overwrite)
720
+ # @param thing [Overwrite] an Overwrite object to apply to this channel
721
+ # @param reason [String] The reason the for defining the overwrite.
722
+ # @overload define_overwrite(thing, allow, deny)
723
+ # @param thing [User, Role] What to define an overwrite for.
724
+ # @param allow [#bits, Permissions, Integer] The permission sets that should receive an `allow` override (i.e. a
725
+ # green checkmark on Discord)
726
+ # @param deny [#bits, Permissions, Integer] The permission sets that should receive a `deny` override (i.e. a red
727
+ # cross on Discord)
728
+ # @param reason [String] The reason the for defining the overwrite.
729
+ # @example Define a permission overwrite for a user that can then mention everyone and use TTS, but not create any invites
730
+ # allow = OnyxCord::Permissions.new
731
+ # allow.can_mention_everyone = true
732
+ # allow.can_send_tts_messages = true
733
+ #
734
+ # deny = OnyxCord::Permissions.new
735
+ # deny.can_create_instant_invite = true
736
+ #
737
+ # channel.define_overwrite(user, allow, deny)
738
+ def define_overwrite(thing, allow = 0, deny = 0, reason: nil)
739
+ unless thing.is_a? Overwrite
740
+ allow_bits = allow.respond_to?(:bits) ? allow.bits : allow
741
+ deny_bits = deny.respond_to?(:bits) ? deny.bits : deny
742
+
743
+ thing = Overwrite.new(thing, allow: allow_bits, deny: deny_bits)
744
+ end
745
+
746
+ API::Channel.update_permission(@bot.token, @id, thing.id, thing.allow.bits, thing.deny.bits, thing.type, reason)
747
+ end
748
+
749
+ # Deletes a permission overwrite for this channel
750
+ # @param target [Member, User, Role, Profile, Recipient, String, Integer] What permission overwrite to delete
751
+ # @param reason [String] The reason the for the overwrite deletion.
752
+ def delete_overwrite(target, reason = nil)
753
+ raise 'Tried deleting a overwrite for an invalid target' unless target.respond_to?(:resolve_id)
754
+
755
+ API::Channel.delete_permission(@bot.token, @id, target.resolve_id, reason)
756
+ end
757
+
758
+ # The list of users currently in this channel. For a voice channel, it will return all the members currently
759
+ # in that channel. For a text channel, it will return all online members that have permission to read it.
760
+ # @return [Array<Member>] the users in this channel
761
+ def users
762
+ if text?
763
+ server.online_members(include_idle: true).select { |u| u.can_read_messages? self }
764
+ elsif voice?
765
+ server.voice_states.filter_map { |id, voice_state| server.member(id) if !voice_state.voice_channel.nil? && voice_state.voice_channel.id == @id }
766
+ end
767
+ end
768
+
769
+ # Retrieves some of this channel's message history.
770
+ # @param amount [Integer] How many messages to retrieve. This must be less than or equal to 100, if it is higher
771
+ # than 100 it will be treated as 100 on Discord's side.
772
+ # @param before_id [Integer] The ID of the most recent message the retrieval should start at, or nil if it should
773
+ # start at the current message.
774
+ # @param after_id [Integer] The ID of the oldest message the retrieval should start at, or nil if it should start
775
+ # as soon as possible with the specified amount.
776
+ # @param around_id [Integer] The ID of the message retrieval should start from, reading in both directions
777
+ # @example Count the number of messages in the last 50 messages that contain the letter 'e'.
778
+ # message_count = channel.history(50).count {|message| message.content.include? "e"}
779
+ # @example Get the last 10 messages before the provided message.
780
+ # last_ten_messages = channel.history(10, message.id)
781
+ # @return [Array<Message>] the retrieved messages.
782
+ def history(amount, before_id = nil, after_id = nil, around_id = nil)
783
+ logs = API::Channel.messages(@bot.token, @id, amount, before_id, after_id, around_id)
784
+ JSON.parse(logs).map { |message| Message.new(message, @bot) }
785
+ end
786
+
787
+ # Retrieves message history, but only message IDs for use with prune.
788
+ # @note For internal use only
789
+ # @!visibility private
790
+ def history_ids(amount, before_id = nil, after_id = nil, around_id = nil)
791
+ logs = API::Channel.messages(@bot.token, @id, amount, before_id, after_id, around_id)
792
+ JSON.parse(logs).map { |message| message['id'].to_i }
793
+ end
794
+
795
+ # Returns a single message from this channel's history by ID.
796
+ # @param message_id [Integer] The ID of the message to retrieve.
797
+ # @return [Message, nil] the retrieved message, or `nil` if it couldn't be found.
798
+ def load_message(message_id)
799
+ raise ArgumentError, 'message_id cannot be nil' if message_id.nil?
800
+
801
+ response = API::Channel.message(@bot.token, @id, message_id)
802
+ Message.new(JSON.parse(response), @bot)
803
+ rescue OnyxCord::Errors::UnknownMessage
804
+ nil
805
+ end
806
+
807
+ alias_method :message, :load_message
808
+
809
+ # Requests the pinned messages in a channel.
810
+ # @param limit [Integer, nil] the limit of how many pinned messages to retrieve. `nil` will return all the pinned messages.
811
+ # @return [Array<Message>] the messages pinned in the channel.
812
+ def pins(limit: 50)
813
+ get_pins = proc do |fetch_limit, before = nil|
814
+ resp = API::Channel.pinned_messages(@bot.token, @id, fetch_limit, before&.iso8601)
815
+ JSON.parse(resp)['items'].map { |pin| Message.new(pin['message'].merge({ 'pinned_at' => pin['pinned_at'] }), @bot) }
816
+ end
817
+
818
+ # Can be done without pagination.
819
+ return get_pins.call(limit) if limit && limit <= 50
820
+
821
+ paginator = Paginator.new(limit, :down) do |last_page|
822
+ if last_page && last_page.count < 50
823
+ []
824
+ else
825
+ get_pins.call(50, last_page&.last&.pinned_at)
826
+ end
827
+ end
828
+
829
+ paginator.to_a
830
+ end
831
+
832
+ # Delete the last N messages on this channel.
833
+ # @param amount [Integer] The amount of message history to consider for pruning. Must be a value between 2 and 100 (Discord limitation)
834
+ # @param strict [true, false] Whether an error should be raised when a message is reached that is too old to be bulk
835
+ # deleted. If this is false only a warning message will be output to the console.
836
+ # @param reason [String, nil] The reason for pruning
837
+ # @raise [ArgumentError] if the amount of messages is not a value between 2 and 100
838
+ # @yield [message] Yields each message in this channels history for filtering the messages to delete
839
+ # @example Pruning messages from a specific user ID
840
+ # channel.prune(100) { |m| m.author.id == 83283213010599936 }
841
+ # @return [Integer] The amount of messages that were successfully deleted
842
+ def prune(amount, strict = false, reason = nil, &block)
843
+ raise ArgumentError, 'Can only delete between 1 and 100 messages!' unless amount.between?(1, 100)
844
+
845
+ messages =
846
+ if block
847
+ history(amount).select(&block).map(&:id)
848
+ else
849
+ history_ids(amount)
850
+ end
851
+
852
+ case messages.size
853
+ when 0
854
+ 0
855
+ when 1
856
+ API::Channel.delete_message(@bot.token, @id, messages.first, reason)
857
+ 1
858
+ else
859
+ bulk_delete(messages, strict, reason)
860
+ end
861
+ end
862
+
863
+ # Deletes a collection of messages
864
+ # @param messages [Array<Message, String, Integer>] the messages (or message IDs) to delete. Total must be an amount between 2 and 100 (Discord limitation)
865
+ # @param strict [true, false] Whether an error should be raised when a message is reached that is too old to be bulk
866
+ # deleted. If this is false only a warning message will be output to the console.
867
+ # @param reason [String, nil] The reason for deleting the messages
868
+ # @raise [ArgumentError] if the amount of messages is not a value between 2 and 100
869
+ # @return [Integer] The amount of messages that were successfully deleted
870
+ def delete_messages(messages, strict = false, reason = nil)
871
+ raise ArgumentError, 'Can only delete between 2 and 100 messages!' unless messages.count.between?(2, 100)
872
+
873
+ messages.map!(&:resolve_id)
874
+ bulk_delete(messages, strict, reason)
875
+ end
876
+
877
+ # Updates the cached permission overwrites
878
+ # @note For internal use only
879
+ # @!visibility private
880
+ def update_overwrites(overwrites)
881
+ @permission_overwrites = overwrites
882
+ end
883
+
884
+ # Add an {Await} for a message in this channel. This is identical in functionality to adding a
885
+ # {OnyxCord::Events::MessageEvent} await with the `in` attribute as this channel.
886
+ # @see Bot#add_await
887
+ # @deprecated Will be changed to blocking behavior in v4.0. Use {#await!} instead.
888
+ def await(key, attributes = {}, &block)
889
+ @bot.add_await(key, OnyxCord::Events::MessageEvent, { in: @id }.merge(attributes), &block)
890
+ end
891
+
892
+ # Add a blocking {Await} for a message in this channel. This is identical in functionality to adding a
893
+ # {OnyxCord::Events::MessageEvent} await with the `in` attribute as this channel.
894
+ # @see Bot#add_await!
895
+ def await!(attributes = {}, &block)
896
+ @bot.add_await!(OnyxCord::Events::MessageEvent, { in: @id }.merge(attributes), &block)
897
+ end
898
+
899
+ # Creates a new invite to this channel.
900
+ # @param max_age [Integer] How many seconds this invite should last.
901
+ # @param max_uses [Integer] How many times this invite should be able to be used.
902
+ # @param temporary [true, false] Whether membership should be temporary (kicked after going offline).
903
+ # @param unique [true, false] If true, Discord will always send a unique invite instead of possibly re-using a similar one
904
+ # @param reason [String] The reason the for the creation of this invite.
905
+ # @return [Invite] the created invite.
906
+ def make_invite(max_age = 0, max_uses = 0, temporary = false, unique = false, reason = nil)
907
+ response = API::Channel.create_invite(@bot.token, @id, max_age, max_uses, temporary, unique, reason)
908
+ Invite.new(JSON.parse(response), @bot)
909
+ end
910
+
911
+ alias_method :invite, :make_invite
912
+
913
+ # Starts typing, which displays the typing indicator on the client for five seconds.
914
+ # If you want to keep typing you'll have to resend this every five seconds. (An abstraction
915
+ # for this will eventually be coming)
916
+ # @example Send a typing indicator for the bot in a given channel.
917
+ # channel.start_typing()
918
+ def start_typing
919
+ API::Channel.start_typing(@bot.token, @id)
920
+ end
921
+
922
+ # Creates a webhook in this channel
923
+ # @param name [String] the default name of this webhook.
924
+ # @param avatar [String] the default avatar URL to give this webhook.
925
+ # @param reason [String] the reason for the webhook creation.
926
+ # @raise [ArgumentError] if the channel isn't a text channel in a server.
927
+ # @return [Webhook] the created webhook.
928
+ def create_webhook(name, avatar = nil, reason = nil)
929
+ raise ArgumentError, 'Tried to create a webhook in a non-server channel' unless server
930
+
931
+ response = API::Channel.create_webhook(@bot.token, @id, name, avatar, reason)
932
+ Webhook.new(JSON.parse(response), @bot)
933
+ end
934
+
935
+ # Requests a list of Webhooks on the channel.
936
+ # @return [Array<Webhook>] webhooks on the channel.
937
+ def webhooks
938
+ raise 'Tried to request webhooks from a non-server channel' unless server
939
+
940
+ webhooks = JSON.parse(API::Channel.webhooks(@bot.token, @id))
941
+ webhooks.map { |webhook_data| Webhook.new(webhook_data, @bot) }
942
+ end
943
+
944
+ # Requests a list of Invites to the channel.
945
+ # @return [Array<Invite>] invites to the channel.
946
+ def invites
947
+ raise 'Tried to request invites from a non-server channel' unless server
948
+
949
+ invites = JSON.parse(API::Channel.invites(@bot.token, @id))
950
+ invites.map { |invite_data| Invite.new(invite_data, @bot) }
951
+ end
952
+
953
+ # Follow the announcement (news) channel to send crossposted messages to a target channel.
954
+ # @param target [Channel, Integer, String] The target channel to send crossposted messages to.
955
+ # @param reason [String, nil] The audit log reason shown for the created webhook in the target channel.
956
+ # @return [Integer] the ID of the created webhook in the target channel.
957
+ def follow(target, reason: nil)
958
+ raise 'Cannot follow a non-announcement channel' unless news?
959
+
960
+ JSON.parse(API::Channel.follow_channel(@bot.token, @id, target.resolve_id, reason))['webhook_id'].to_i
961
+ end
962
+
963
+ # Returns the last message or forum post created in this channel.
964
+ # @return [Message, Channel, nil] the last message sent in this channel,
965
+ # the most recent forum post if this is a forum or media channel, or `nil`.
966
+ def last_message
967
+ return unless @last_message_id
968
+
969
+ if forum? || media?
970
+ @bot.channel(@last_message_id)
971
+ else
972
+ load_message(@last_message_id)
973
+ end
974
+ end
975
+
976
+ # Start a thread.
977
+ # @param name [String] The name of the thread.
978
+ # @param auto_archive_duration [60, 1440, 4320, 10080] How long before a thread is automatically
979
+ # archived.
980
+ # @param message [Message, Integer, String] The message to reference when starting this thread.
981
+ # @param type [Symbol, Integer] The type of thread to create. Can be a key from {TYPES} or the value.
982
+ # @return [Channel]
983
+ def start_thread(name, auto_archive_duration, message: nil, type: 11)
984
+ message_id = message&.id || message
985
+ type = TYPES[type] || type
986
+
987
+ data = if message
988
+ API::Channel.start_thread_with_message(@bot.token, @id, message_id, name, auto_archive_duration)
989
+ else
990
+ API::Channel.start_thread_without_message(@bot.token, @id, name, auto_archive_duration, type)
991
+ end
992
+
993
+ @bot.ensure_channel(JSON.parse(data))
994
+ end
995
+
996
+ # Start a thread in a forum or media channel.
997
+ # @param name [String] The name of the forum post to create.
998
+ # @param auto_archive_duration [Integer, nil] How long before the post is automatically archived.
999
+ # @param rate_limit_per_user [Integer, nil] The slowmode rate of the forum post to create.
1000
+ # @param tags [Array<#resolve_id>, nil] The tags of the forum channel to apply onto the forum post.
1001
+ # @param content [String, nil] The content of the forum post's starter message.
1002
+ # @param embeds [Array<Hash, Webhooks::Embed>, nil] The embeds that should be attached to the forum post's starter message.
1003
+ # @param allowed_mentions [Hash, OnyxCord::AllowedMentions, nil] Mentions that are allowed to ping on this forum post's starter message.
1004
+ # @param components [Webhooks::View, Array<#to_h>, nil] The interaction components to associate with this forum post's starter message.
1005
+ # @param stickers [Array<#resolve_id>, nil] The stickers to include in the forum post's starter message.
1006
+ # @param attachments [Array<File>, nil] Files that can be referenced in embeds and components via `attachment://file.png`.
1007
+ # @param flags [Integer, Symbol, Array<Symbol, Integer>, nil] The flags to set on the forum post's starter message. Currently only `:suppress_embeds` (1 << 2), `:suppress_notifications` (1 << 12), and `:uikit_components` (1 << 15) can be set.
1008
+ # @param has_components [true, false] Whether the starter message for this forum post includes any V2 components. Enabling this disables sending content and embeds.
1009
+ # @param reason [String, nil] The reason for creating this forum post.
1010
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the builder overwrite method data.
1011
+ # @yieldparam view [Webhooks::View] An optional component builder. Arguments passed to the builder overwrite method data.
1012
+ # @return [Message] the starter message of the forum post. The forum post that was created can be accessed via {Message#thread}.
1013
+ def start_forum_thread(name:, auto_archive_duration: nil, rate_limit_per_user: nil, tags: nil, content: nil, embeds: nil, allowed_mentions: nil, components: nil, stickers: nil, attachments: nil, flags: nil, has_components: false, components_v2: false, reason: nil)
1014
+ builder = OnyxCord::Webhooks::Builder.new
1015
+ view = OnyxCord::Webhooks::View.new
1016
+
1017
+ builder.content = content
1018
+ embeds&.each { |embed| builder << embed }
1019
+ builder.allowed_mentions = allowed_mentions
1020
+
1021
+ yield(builder, view) if block_given?
1022
+
1023
+ components = components&.to_a || view.to_a
1024
+ flags = Array(flags).map { |flag| OnyxCord::Message::FLAGS[flag] || flag }.reduce(0, &:|)
1025
+ flags = OnyxCord::MessageComponents.apply_v2_flag(flags, components, force: has_components || components_v2)
1026
+ builder = builder.to_json_hash
1027
+
1028
+ message = { content: builder[:content], embeds: builder[:embeds], allowed_mentions: builder[:allowed_mentions], components: components, sticker_ids: stickers&.map(&:resolve_id), flags: flags }
1029
+ response = JSON.parse(API::Channel.start_thread_in_forum_or_media_channel(@bot.token, @id, name, message.compact, attachments, rate_limit_per_user, auto_archive_duration, tags&.map(&:resolve_id), reason))
1030
+
1031
+ Message.new(response['message'].merge!('channel_id' => response['id'], 'thread' => response), @bot)
1032
+ end
1033
+
1034
+ def default_reaction
1035
+ @default_reaction.is_a?(Integer) ? server.emojis[@default_reaction] : @default_reaction
1036
+ end
1037
+
1038
+ # Get a tag in this forum or media channel.
1039
+ # @param id [String, Integer] The ID of the tag to find.
1040
+ # @return [ChannelTag, nil] The tag that was found or `nil` if it couldn't be found.
1041
+ def tag(id)
1042
+ id = id.resolve_id
1043
+ @available_tags.find { |tag| tag == id }
1044
+ end
1045
+
1046
+ # Check if a specific tag has been applied to this thread.
1047
+ # @param id [String, Integer, ChannelTag] The tag you want to check.
1048
+ # @return [true, false] Whether or not the thread has the tag applied.
1049
+ def tag?(id)
1050
+ @applied_tags.any?(id&.resolve_id)
1051
+ end
1052
+
1053
+ # Get the tags for this channel. If this channel is a thread channel,
1054
+ # then the tags that have been applied to the thread will be returned,
1055
+ # and if the channel is a forum or media channel, then the tags that can
1056
+ # be applied onto threads created in this channel will be returned instead.
1057
+ # @return [Array<ChannelTag>] The available or set channel tags for this channel.
1058
+ def tags
1059
+ return @available_tags if forum? || !thread_only?
1060
+
1061
+ @applied_tags.filter_map { |tag_id| parent&.tag(tag_id) }
1062
+ end
1063
+
1064
+ # Add one or more tags to this thread channel.
1065
+ # @param tags [Array, Integer, String, ChannelTag] The tags to add to the thread.
1066
+ def add_tags(tags, reason: nil)
1067
+ raise 'Cannot add tags to this channel' unless parent&.thread_only?
1068
+
1069
+ modify(tags: @applied_tags + Array(tags).map(&:resolve_id), reason: reason)
1070
+ end
1071
+
1072
+ alias_method :add_tag, :add_tags
1073
+
1074
+ # Remove one or more tag from this thread channel.
1075
+ # @param tags [Array, Integer, String, ChannelTag] The tags to remove from the thread.
1076
+ def remove_tags(tags, reason: nil)
1077
+ raise 'Cannot remove tags from this channel' unless parent&.thread_only?
1078
+
1079
+ modify(tags: @applied_tags - Array(tags).map(&:resolve_id), reason: reason)
1080
+ end
1081
+
1082
+ alias_method :remove_tag, :remove_tags
1083
+
1084
+ # Create a tag in this forum or media channel.
1085
+ # @param name [String] The 1-20 character name of the tag to create.
1086
+ # @param moderated [true, false] Whether or not the tag should be moderated.
1087
+ # @param emoji [Emoji, Integer, String, nil] An optional emoji to set for the tag.
1088
+ # @param reason [String, nil] The reason to show in the audit log for creating the tag.
1089
+ # @return [nil]
1090
+ def create_tag(name:, moderated:, emoji: nil, reason: nil)
1091
+ update_tags({ name:, moderated:, **Emoji.build_emoji_hash(emoji) }, reason) if thread_only?
1092
+ end
1093
+
1094
+ # @!group Threads
1095
+
1096
+ # Join this thread.
1097
+ def join_thread
1098
+ @bot.join_thread(@id)
1099
+ end
1100
+
1101
+ # Leave this thread
1102
+ def leave_thread
1103
+ @bot.leave_thread(@id)
1104
+ end
1105
+
1106
+ # Members in the thread.
1107
+ def members
1108
+ @bot.thread_members[@id].collect { |id| @server_id ? @bot.member(@server_id, id) : @bot.user(id) }
1109
+ end
1110
+
1111
+ # Add a member to the thread
1112
+ # @param member [Member, Integer, String] The member, or ID of the member, to add to this thread.
1113
+ def add_member(member)
1114
+ @bot.add_thread_member(@id, member)
1115
+ end
1116
+
1117
+ # @param member [Member, Integer, String] The member, or ID of the member, to remove from a thread.
1118
+ def remove_member(member)
1119
+ @bot.remove_thread_member(@id, member)
1120
+ end
1121
+
1122
+ # @!endgroup
1123
+
1124
+ # Modify the properties of the channel.
1125
+ # @param name [String] The new 1-100 character name of the channel.
1126
+ # @param type [Integer, Symbol] The new type of the channel. You can only convert between text and announcement channels.
1127
+ # @param topic [String, nil] The 0-1024 character topic of the channel; 0-4096 characters for forum channels.
1128
+ # @param nsfw [true, false, nil] Whether or not the channel should be marked as age-restricted.
1129
+ # @param rate_limit_per_user [Integer, nil] The new slowmode-rate of the channel; between 0-21600 (in seconds).
1130
+ # @param bitrate [Integer, nil] The new bitrate of the voice or stage channel; minimum of 8000 (in bits).
1131
+ # @param user_limit [Integer, nil] The maximum number of users who can join the voice or stage channel; 0 for no limit.
1132
+ # @param permission_overwrites [Array<Overwrite, Hash, #to_hash>, nil] The new permission overwrites to set for the channel.
1133
+ # @param parent [Channel, Integer, String, nil] The new category channel to set, or `nil` to orphan the chnanel.
1134
+ # @param voice_region [VoiceRegion, String, nil] The new voice region to set for the voice or stage channel.
1135
+ # @param video_quality_mode [Symbol, Integer, nil] The new camera video quality mode to set for the voice or stage channel.
1136
+ # @param default_auto_archive_duration [Integer, nil] The default client-side duration before a thread is archived due to inactivity.
1137
+ # @param flags [Symbol, Integer, Array<Symbol, Integer>] The flags to set for the channel.
1138
+ # @param tags [Array<ChannelTag, #to_h, #resolve_id>] The tags to set on the thread channel, or the new tags that will be available in the forum channel.
1139
+ # @param default_reaction [Integer, String, Emoji, nil] The emoji to display on threads created in the forum channel.
1140
+ # @param default_sort_order [Integer, Symbol, nil] The default order used to order threads in the forum channel.
1141
+ # @param default_forum_layout [Integer, Symbol] The default layout type used to display threads in the forum channel.
1142
+ # @param archived [true, false] Whether or not the thread should be archived.
1143
+ # @param locked [true, false] Whether or not the thread should be locked.
1144
+ # @param invitable [true, false] Whether or not non-moderators should be able to add other non-moderators to the private thread.
1145
+ # @param add_flags [Symbol, Integer, Array<Symbol, Integer>] The flags to add to the channel. Mutually exclusive with `flags:`.
1146
+ # @param remove_flags [Symbol, Integer, Array<Symbol, Integer>] The flags to remove from the channel. Mutually exclusive with `flags:`.
1147
+ # @param position [Integer, nil] The new sorting position of the channel. Generally, this parameter should not be used. Please use {#sort_after} instead.
1148
+ # @param auto_archive_duration [Integer] The amount of minutes after which the thread will stop showing in the channel list.
1149
+ # @param default_thread_rate_limit_per_user [Integer] The default slowmode rate to set on threads created in the text or forum channel.
1150
+ # @param reason [String, nil] The reason to show in the server's audit log for modifying the channel.
1151
+ # @return [nil]
1152
+ def modify(
1153
+ name: :undef, type: :undef, topic: :undef, nsfw: :undef, rate_limit_per_user: :undef, bitrate: :undef,
1154
+ user_limit: :undef, permission_overwrites: :undef, parent: :undef, voice_region: :undef, video_quality_mode: :undef,
1155
+ default_auto_archive_duration: :undef, flags: :undef, tags: :undef, default_reaction: :undef, default_sort_order: :undef,
1156
+ default_forum_layout: :undef, archived: :undef, locked: :undef, invitable: :undef, add_flags: :undef, remove_flags: :undef,
1157
+ position: :undef, auto_archive_duration: :undef, default_thread_rate_limit_per_user: :undef, reason: nil
1158
+ )
1159
+ data = {
1160
+ name: name,
1161
+ type: TYPES[type] || type,
1162
+ topic: topic,
1163
+ nsfw: nsfw,
1164
+ position: position,
1165
+ rate_limit_per_user: rate_limit_per_user,
1166
+ bitrate: bitrate,
1167
+ user_limit: user_limit,
1168
+ permission_overwrites: permission_overwrites == :undef ? permission_overwrites : permission_overwrites&.map(&:to_hash),
1169
+ parent_id: parent == :undef ? parent : parent&.resolve_id,
1170
+ rtc_region: voice_region == :undef ? voice_region : voice_region&.to_s,
1171
+ video_quality_mode: VIDEO_QUALITY_MODES[video_quality_mode] || video_quality_mode,
1172
+ default_auto_archive_duration: default_auto_archive_duration,
1173
+ default_reaction_emoji: default_reaction == :undef ? default_reaction : Emoji.build_emoji_hash(default_reaction),
1174
+ default_sort_order: FORUM_SORT_ORDERS[default_sort_order] || default_sort_order,
1175
+ default_forum_layout: FORUM_LAYOUTS[default_forum_layout] || default_forum_layout,
1176
+ archived: archived,
1177
+ flags: flags == :undef ? flags : Array(flags).map { |bit| FLAGS[bit] || bit.to_i }.reduce(&:|),
1178
+ default_thread_rate_limit_per_user: default_thread_rate_limit_per_user,
1179
+ auto_archive_duration: auto_archive_duration,
1180
+ locked: locked,
1181
+ invitable: invitable
1182
+ }
1183
+
1184
+ if tags != :undef && (thread_only? || thread?)
1185
+ tags = (thread? ? tags&.map(&:resolve_id) : tags&.map(&:to_h))
1186
+
1187
+ data[thread_only? ? :available_tags : :applied_tags] = tags
1188
+ end
1189
+
1190
+ if data[:type] != :undef
1191
+ if news? && data[:type] != TYPES[:text]
1192
+ raise ArgumentError, 'Can only convert news channels to text channels'
1193
+ elsif text? && data[:type] != TYPES[:news]
1194
+ raise ArgumentError, 'Can only convert text channels to news channels'
1195
+ elsif !text? && !news?
1196
+ raise ArgumentError, 'Can only convert between text and news channels'
1197
+ end
1198
+ end
1199
+
1200
+ if add_flags != :undef || remove_flags != :undef
1201
+ raise ArgumentError, "'add_flags' and 'remove_flags' cannot be used with 'flags'" if flags != :undef
1202
+
1203
+ to_flags = lambda do |value|
1204
+ [*(value == :undef ? 0 : value)].map { |bit| FLAGS[bit] || bit.to_i }.reduce(&:|)
1205
+ end
1206
+
1207
+ data[:flags] = ((@flags & ~to_flags.call(remove_flags)) | to_flags.call(add_flags))
1208
+ end
1209
+
1210
+ update_data(JSON.parse(API::Channel.update!(@bot.token, @id, **data, reason: reason)))
1211
+ nil
1212
+ end
1213
+
1214
+ # The default `inspect` method is overwritten to give more useful output.
1215
+ def inspect
1216
+ "<Channel name=#{@name} id=#{@id} topic=\"#{@topic}\" type=#{@type} position=#{@position} server=#{@server || @server_id}>"
1217
+ end
1218
+
1219
+ # Set the last pin timestamp of a channel.
1220
+ # @param time [String, nil] the time of the last pinned message in the channel
1221
+ # @note For internal use only
1222
+ # @!visibility private
1223
+ def process_last_pin_timestamp(time)
1224
+ @last_pin_timestamp = time ? Time.parse(time) : time
1225
+ end
1226
+
1227
+ # Set the last message ID of a channel.
1228
+ # @param id [Integer, nil] the ID of the last message in a channel
1229
+ # @note For internal use only
1230
+ # @!visibility private
1231
+ def process_last_message_id(id)
1232
+ @last_message_id = id
1233
+ end
1234
+
1235
+ # Set the available tags of a channel.
1236
+ # @param tag [Hash] the data for the tag to create
1237
+ # @param reason [String, nil] the reason to show in the audit log
1238
+ # @note For internal use only
1239
+ # @!visibility private
1240
+ def update_tags(tag, reason)
1241
+ raise 'Cannot execute action on channel' unless thread_only?
1242
+
1243
+ tags = @available_tags.dup.tap { |old| old.delete(tag[:id]) }
1244
+
1245
+ modify(tags: (tag[:d] ? tags : (tags << tag)), reason: reason)
1246
+ end
1247
+
1248
+ # Updates the cached data with new data
1249
+ # @note For internal use only
1250
+ # @!visibility private
1251
+ def update_data(new_data = nil)
1252
+ new_data ||= JSON.parse(API::Channel.resolve(@bot.token, @id))
1253
+ @type = new_data['type'] || 0
1254
+ @topic = new_data['topic']
1255
+ @bitrate = new_data['bitrate']
1256
+ @name = new_data['name'] || @name
1257
+ @user_limit = new_data['user_limit']
1258
+
1259
+ @position = new_data['position']
1260
+ @parent_id = new_data['parent_id']&.to_i
1261
+ @nsfw = new_data['nsfw'] || false
1262
+ @rate_limit_per_user = new_data['rate_limit_per_user'] || 0
1263
+ @message_count = new_data['message_count']
1264
+ @member_count = new_data['member_count']
1265
+
1266
+ @total_message_sent = new_data['total_message_sent'] || 0
1267
+ @flags = new_data['flags'] || 0
1268
+ @voice_region = new_data['rtc_region']
1269
+ @video_quality_mode = new_data['video_quality_mode']
1270
+ @last_message_id = new_data['last_message_id']&.to_i
1271
+
1272
+ @default_auto_archive_duration = new_data['default_auto_archive_duration']
1273
+ @default_sort_order = new_data['default_sort_order']
1274
+ @default_forum_layout = new_data['default_forum_layout']
1275
+ @default_thread_rate_limit_per_user = new_data['default_thread_rate_limit_per_user']
1276
+
1277
+ if (metadata = new_data['thread_metadata'])
1278
+ @archived = metadata['archived']
1279
+ @auto_archive_duration = metadata['auto_archive_duration']
1280
+ @archive_timestamp = Time.iso8601(metadata['archive_timestamp'])
1281
+ @locked = metadata['locked']
1282
+ @invitable = metadata['invitable']
1283
+ end
1284
+
1285
+ @applied_tags = new_data['applied_tags']&.map(&:to_i) || []
1286
+
1287
+ process_available_tags(new_data['available_tags'])
1288
+ process_last_pin_timestamp(new_data['last_pin_timestamp'])
1289
+ process_permission_overwrites(new_data['permission_overwrites'])
1290
+ process_default_reaction_emoji(new_data['default_reaction_emoji'])
1291
+ end
1292
+
1293
+ # @return [String] a URL that a user can use to navigate to this channel in the client
1294
+ def link
1295
+ "https://discord.com/channels/#{@server_id || '@me'}/#{@id}"
1296
+ end
1297
+
1298
+ alias_method :jump_link, :link
1299
+
1300
+ private
1301
+
1302
+ TWO_WEEKS = 86_400 * 14
1303
+ private_constant :TWO_WEEKS
1304
+
1305
+ # Deletes a list of messages on this channel using bulk delete.
1306
+ def bulk_delete(ids, strict = false, reason = nil)
1307
+ min_snowflake = IDObject.synthesise(Time.now - TWO_WEEKS)
1308
+
1309
+ ids.reject! do |e|
1310
+ next unless e < min_snowflake
1311
+
1312
+ message = "Attempted to bulk_delete message #{e} which is too old (min = #{min_snowflake})"
1313
+ raise ArgumentError, message if strict
1314
+
1315
+ OnyxCord::LOGGER.warn(message)
1316
+ true
1317
+ end
1318
+
1319
+ API::Channel.bulk_delete_messages(@bot.token, @id, ids, reason)
1320
+ ids.size
1321
+ end
1322
+
1323
+ # @!visibility private
1324
+ def process_permission_overwrites(overwrites)
1325
+ # Populate permission overwrites
1326
+ @permission_overwrites = {}
1327
+
1328
+ overwrites&.each do |element|
1329
+ id = element['id'].to_i
1330
+ @permission_overwrites[id] = Overwrite.from_hash(element)
1331
+ end
1332
+ end
1333
+
1334
+ # @!visibility private
1335
+ def process_available_tags(tags)
1336
+ # Populate available tags
1337
+ @available_tags = []
1338
+
1339
+ tags&.each do |element|
1340
+ @available_tags << ChannelTag.new(element, self, @bot)
1341
+ end
1342
+ end
1343
+
1344
+ # @!visibility private
1345
+ def process_default_reaction_emoji(emoji)
1346
+ return @default_reaction = nil unless emoji
1347
+
1348
+ @default_reaction = if (name = emoji['emoji_name'])
1349
+ Emoji.new({ 'name' => name }, @bot)
1350
+ else
1351
+ emoji['emoji_id']&.to_i
1352
+ end
1353
+ end
1354
+ end
1355
+ end