discordrb 3.6.0 → 3.7.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +10 -4
  3. data/.rubocop.yml +7 -0
  4. data/CHANGELOG.md +60 -3
  5. data/discordrb.gemspec +1 -1
  6. data/lib/discordrb/api/channel.rb +3 -3
  7. data/lib/discordrb/api/server.rb +2 -2
  8. data/lib/discordrb/api/user.rb +9 -16
  9. data/lib/discordrb/api.rb +5 -0
  10. data/lib/discordrb/bot.rb +24 -4
  11. data/lib/discordrb/container.rb +15 -0
  12. data/lib/discordrb/data/channel.rb +72 -2
  13. data/lib/discordrb/data/collectibles.rb +5 -3
  14. data/lib/discordrb/data/emoji.rb +1 -1
  15. data/lib/discordrb/data/interaction.rb +41 -9
  16. data/lib/discordrb/data/member.rb +39 -5
  17. data/lib/discordrb/data/message.rb +52 -25
  18. data/lib/discordrb/data/overwrite.rb +2 -3
  19. data/lib/discordrb/data/primary_server.rb +1 -1
  20. data/lib/discordrb/data/profile.rb +21 -40
  21. data/lib/discordrb/data/reaction.rb +25 -1
  22. data/lib/discordrb/data/role_subscription.rb +41 -0
  23. data/lib/discordrb/data/server.rb +6 -4
  24. data/lib/discordrb/data.rb +1 -0
  25. data/lib/discordrb/events/interactions.rb +7 -1
  26. data/lib/discordrb/events/message.rb +32 -3
  27. data/lib/discordrb/events/reactions.rb +58 -0
  28. data/lib/discordrb/gateway.rb +2 -5
  29. data/lib/discordrb/id_object.rb +1 -1
  30. data/lib/discordrb/permissions.rb +2 -3
  31. data/lib/discordrb/version.rb +1 -1
  32. data/lib/discordrb/voice/network.rb +64 -52
  33. data/lib/discordrb/voice/opcodes.rb +29 -0
  34. data/lib/discordrb/voice/sodium.rb +137 -78
  35. data/lib/discordrb/voice/timer.rb +19 -0
  36. data/lib/discordrb/voice/voice_bot.rb +20 -42
  37. data/lib/discordrb.rb +3 -1
  38. metadata +10 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 22a5d0696568b896315a4a63cbd262028f24cba3406461f1f9b59764194153a5
4
- data.tar.gz: f5f516dd8e0e25512df14ab6a8b6ce68dc326e821ac92b3d9fae9f6fa84924ea
3
+ metadata.gz: 308d91242127ada263e1c5a6287e51ed1ed192a7b022ade21546a8fba2f1df70
4
+ data.tar.gz: 497a356aa8221eda73637362516416eedd985732bded656e2e403968d0b5dacc
5
5
  SHA512:
6
- metadata.gz: 92fe5293a9deb5ebd253f579926ea9741835e4b019fa9cc5ae398ac3975a6d040ef611316bde03f07eabbc09cd78a468931e9e2c07a1448e5d4339cdbbbbd1b1
7
- data.tar.gz: 456d37be600b66b43ecfa38b31d0f72113b308ebcc32f54d407f65ee6828d17b3f38ec495799eace92bc2951efcef121ad3ba572dab70bd848efd40f17293025
6
+ metadata.gz: 33154b317faa6f1a61ff0c290a71405aacd49520a3d60c39421b0aae54e6304decb647430f0291d07885d4c5883729110b87c41ec977088bf1dfb39066c4785d
7
+ data.tar.gz: 4355fb5dc5af9007ba18e1a00980862d619c88757ab88c4b10ecda2f051714d85b602e7fe3726d8d31cdca61b1f47b9b17e50999b23de72c1663f0c5531efffe
@@ -8,11 +8,19 @@ jobs:
8
8
  publish:
9
9
  runs-on: ubuntu-latest
10
10
  permissions:
11
- contents: read
12
11
  packages: write
12
+ id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
13
+ contents: write # IMPORTANT: this permission is required for `rake release` to push the release tag
13
14
 
14
15
  steps:
15
- - uses: actions/checkout@v4
16
+ - uses: actions/checkout@v5
17
+ with:
18
+ persist-credentials: false
19
+ - name: Set up Ruby
20
+ uses: ruby/setup-ruby@v1
21
+ with:
22
+ bundler-cache: true
23
+ ruby-version: ruby
16
24
 
17
25
  - name: Validate tag matches gemspecs
18
26
  run: |
@@ -26,8 +34,6 @@ jobs:
26
34
  done
27
35
 
28
36
  - uses: rubygems/release-gem@v1
29
- with:
30
- api_key: ${{ secrets.RUBYGEMS_API_KEY }}
31
37
 
32
38
  - name: Publish to GitHub Packages
33
39
  env:
data/.rubocop.yml CHANGED
@@ -109,6 +109,10 @@ Style/RedundantReturn:
109
109
  Style/RedundantParentheses:
110
110
  Enabled: false
111
111
 
112
+ Style:/EndOfLine:
113
+ Enabled: true
114
+ EnforcedStyle: lf
115
+
112
116
  # This will probably be a breaking change, but should happen
113
117
  Style/ReturnNilInPredicateMethodDefinition:
114
118
  Enabled: false
@@ -116,3 +120,6 @@ Style/ReturnNilInPredicateMethodDefinition:
116
120
  # FIXME: Disabled due to breaking tests, should probably refactor the code instead
117
121
  Style/SafeNavigation:
118
122
  Enabled: false
123
+
124
+ Style/Documentation:
125
+ Enabled: true
data/CHANGELOG.md CHANGED
@@ -5,15 +5,72 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.7.0] - 2025-10-22
9
+
10
+ [3.7.0]: https://github.com/shardlab/discordrb/releases/tag/v3.7.0
11
+
12
+ [View diff for this release.](https://github.com/shardlab/discordrb/compare/v3.6.1..v3.7.0)
13
+
14
+ ### Summary
15
+
16
+ #### What's Changed
17
+
18
+ - feat: forking #262 by @Droid00000 in https://github.com/shardlab/discordrb/pull/354
19
+ - Add support for role mentions on the bot's autogenerated managed roleid. by @Ifiht in https://github.com/shardlab/discordrb/pull/262
20
+ - feat: Member#sort_roles by @Droid00000 in https://github.com/shardlab/discordrb/pull/370
21
+ - fix: lazily resolve resolved message channels by @Droid00000 in https://github.com/shardlab/discordrb/pull/352
22
+ - fix: widget IVAR by @Droid00000 in https://github.com/shardlab/discordrb/pull/368
23
+ - feat: more missing channel fields by @Droid00000 in https://github.com/shardlab/discordrb/pull/367
24
+ - feat: per-server avatars, banners, and bio for bots by @Droid00000 in https://github.com/shardlab/discordrb/pull/350
25
+ - fix: broken guard clauses by @Droid00000 in https://github.com/shardlab/discordrb/pull/329
26
+ - feat: missing interaction attributes by @Droid00000 in https://github.com/shardlab/discordrb/pull/330
27
+ - fix: update Member#add_role and Member#remove_role by @swarley in https://github.com/shardlab/discordrb/pull/379
28
+ - feat: add ability to send webhook messages to threads by @dmogilevsky in https://github.com/shardlab/discordrb/pull/385
29
+ - feat: static nameplate URLs by @Droid00000 in https://github.com/shardlab/discordrb/pull/386
30
+ - fix: prevent #channels from being nil by @Droid00000 in https://github.com/shardlab/discordrb/pull/383
31
+ - Support base64 versions "0.x" by @miry in https://github.com/shardlab/discordrb/pull/380
32
+ - fix: editing application command permissions by @Droid00000 in https://github.com/shardlab/discordrb/pull/381
33
+ - feat: burst reactions by @Droid00000 in https://github.com/shardlab/discordrb/pull/321
34
+ - fix: multipart webhooks by @Droid00000 in https://github.com/shardlab/discordrb/pull/390
35
+ - fix: key -> key? by @Droid00000 in https://github.com/shardlab/discordrb/pull/388
36
+ - feat: move to aead_xchacha20_poly1305_rtpsize voice encoding by @swarley in https://github.com/shardlab/discordrb/pull/318
37
+ - feat: new timestamp styles by @Droid00000 in https://github.com/shardlab/discordrb/pull/384
38
+ - removal: undocumented gateway properties by @Droid00000 in https://github.com/shardlab/discordrb/pull/333
39
+ - feat: more message fields by @Droid00000 in https://github.com/shardlab/discordrb/pull/389
40
+
41
+ #### New Contributors
42
+
43
+ - @Ifiht made their first contribution in https://github.com/shardlab/discordrb/pull/262
44
+ - @dmogilevsky made their first contribution in https://github.com/shardlab/discordrb/pull/385
45
+ - @miry made their first contribution in https://github.com/shardlab/discordrb/pull/380
46
+
47
+ **Full Changelog**: https://github.com/shardlab/discordrb/compare/v3.6.1...v3.7.0
48
+
49
+ ## [3.6.1] - 2025-10-22
50
+
51
+ [3.6.1]: https://github.com/shardlab/discordrb/releases/tag/v3.6.1
52
+
53
+ [View diff for this release.](https://github.com/shardlab/discordrb/compare/v3.6.0..v3.6.1)
54
+
55
+ ### Summary
56
+
57
+ #### What's Changed
58
+
59
+ - fix: update rubygems release workflow by @swarley in https://github.com/shardlab/discordrb/pull/373
60
+ - fix: update how allowed mentions is passed to message edits by @swarley in https://github.com/shardlab/discordrb/pull/374
61
+ - fix: release workflow action by @swarley in https://github.com/shardlab/discordrb/pull/375
62
+
63
+ **Full Changelog**: https://github.com/shardlab/discordrb/compare/v3.6.0...v3.6.1
64
+
8
65
  ## [3.6.0] - 2025-10-22
9
66
 
10
- [3.5.0]: https://github.com/shardlab/discordrb/releases/tag/v3.6.0
67
+ [3.6.0]: https://github.com/shardlab/discordrb/releases/tag/v3.6.0
11
68
 
12
69
  [View diff for this release.](https://github.com/shardlab/discordrb/compare/v3.5.0..v3.6.0)
13
70
 
14
71
  ### Summary
15
72
 
16
- ## What's Changed
73
+ #### What's Changed
17
74
 
18
75
  - feat: Bot#invite_url - scope & redirect_uri params by @Birdie0 in https://github.com/shardlab/discordrb/pull/241
19
76
  - Update ping.rb by @gustavomgama in https://github.com/shardlab/discordrb/pull/243
@@ -83,7 +140,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
83
140
  - feat: fix thread caching by @Droid00000 in https://github.com/shardlab/discordrb/pull/363
84
141
  - feat: gh actions by @swarley in https://github.com/shardlab/discordrb/pull/371
85
142
 
86
- ## New Contributors
143
+ #### New Contributors
87
144
 
88
145
  - @gustavomgama made their first contribution in https://github.com/shardlab/discordrb/pull/243
89
146
  - @wouterdedroog made their first contribution in https://github.com/shardlab/discordrb/pull/249
data/discordrb.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  }
29
29
  spec.require_paths = ['lib']
30
30
 
31
- spec.add_dependency 'base64', '~> 0.2.0'
31
+ spec.add_dependency 'base64', '~> 0.2'
32
32
  spec.add_dependency 'ffi', '>= 1.9.24'
33
33
  spec.add_dependency 'opus-ruby'
34
34
  spec.add_dependency 'rest-client', '>= 2.0.0'
@@ -117,7 +117,7 @@ module Discordrb::API::Channel
117
117
 
118
118
  # Edit a message
119
119
  # https://discord.com/developers/docs/resources/channel#edit-message
120
- def edit_message(token, channel_id, message_id, message, mentions = [], embeds = nil, components = nil, flags = nil)
120
+ def edit_message(token, channel_id, message_id, message, mentions = nil, embeds = nil, components = nil, flags = nil)
121
121
  Discordrb::API.request(
122
122
  :channels_cid_messages_mid,
123
123
  channel_id,
@@ -212,9 +212,9 @@ module Discordrb::API::Channel
212
212
 
213
213
  # Get a list of clients who reacted with a specific reaction on a message
214
214
  # https://discord.com/developers/docs/resources/channel#get-reactions
215
- def get_reactions(token, channel_id, message_id, emoji, before_id, after_id, limit = 100)
215
+ def get_reactions(token, channel_id, message_id, emoji, before_id, after_id, limit = 100, type = 0)
216
216
  emoji = URI.encode_www_form_component(emoji) unless emoji.ascii_only?
217
- query_string = URI.encode_www_form({ limit: limit || 100, before: before_id, after: after_id }.compact)
217
+ query_string = URI.encode_www_form({ limit: limit || 100, before: before_id, after: after_id, type: type }.compact)
218
218
  Discordrb::API.request(
219
219
  :channels_cid_messages_mid_reactions_emoji,
220
220
  channel_id,
@@ -147,13 +147,13 @@ module Discordrb::API::Server
147
147
 
148
148
  # Update the current member's properties.
149
149
  # https://discord.com/developers/docs/resources/guild#modify-current-member
150
- def update_current_member(token, server_id, nick = :undef, reason = nil)
150
+ def update_current_member(token, server_id, nick = :undef, reason = nil, bio = :undef, banner = :undef, avatar = :undef)
151
151
  Discordrb::API.request(
152
152
  :guilds_sid_members_me,
153
153
  server_id,
154
154
  :patch,
155
155
  "#{Discordrb::API.api_base}/guilds/#{server_id}/members/@me",
156
- { nick: nick }.reject { |_, v| v == :undef }.to_json,
156
+ { nick: nick, bio: bio, banner: banner, avatar: avatar }.reject { |_, v| v == :undef }.to_json,
157
157
  Authorization: token,
158
158
  content_type: :json,
159
159
  'X-Audit-Log-Reason': reason
@@ -43,15 +43,21 @@ module Discordrb::API::User
43
43
  )
44
44
  end
45
45
 
46
- # Update user data
46
+ # @deprecated Please use {update_current_user} instead.
47
47
  # https://discord.com/developers/docs/resources/user#modify-current-user
48
- def update_profile(token, email, password, new_username, avatar, new_password = nil)
48
+ def update_profile(token, _email, _password, new_username, avatar, _new_password = nil)
49
+ update_current_user(token, new_username, avatar)
50
+ end
51
+
52
+ # Update the properties of the user for the current bot.
53
+ # https://discord.com/developers/docs/resources/user#modify-current-user
54
+ def update_current_user(token, username = :undef, avatar = :undef, banner = :undef)
49
55
  Discordrb::API.request(
50
56
  :users_me,
51
57
  nil,
52
58
  :patch,
53
59
  "#{Discordrb::API.api_base}/users/@me",
54
- { avatar: avatar, email: email, new_password: new_password, password: password, username: new_username }.to_json,
60
+ { username: username, avatar: avatar, banner: banner }.reject { |_, value| value == :undef }.to_json,
55
61
  Authorization: token,
56
62
  content_type: :json
57
63
  )
@@ -119,19 +125,6 @@ module Discordrb::API::User
119
125
  )
120
126
  end
121
127
 
122
- # Change user status setting
123
- def change_status_setting(token, status)
124
- Discordrb::API.request(
125
- :users_me_settings,
126
- nil,
127
- :patch,
128
- "#{Discordrb::API.api_base}/users/@me/settings",
129
- { status: status }.to_json,
130
- Authorization: token,
131
- content_type: :json
132
- )
133
- end
134
-
135
128
  # Returns one of the "default" discord avatars from the CDN given a discriminator or id since new usernames
136
129
  # TODO: Maybe change this method again after discriminator removal ?
137
130
  def default_avatar(discrim_id = 0, legacy: false)
data/lib/discordrb/api.rb CHANGED
@@ -241,6 +241,11 @@ module Discordrb::API
241
241
  "#{cdn_url}/avatar-decoration-presets/#{avatar_decoration_id}.#{format}"
242
242
  end
243
243
 
244
+ # make a static nameplate URL from the nameplate asset.
245
+ def static_nameplate_url(nameplate_asset, format = 'png')
246
+ "#{cdn_url}/assets/collectibles/#{nameplate_asset.delete_suffix('/')}/static.#{format}"
247
+ end
248
+
244
249
  # make a nameplate URL from the nameplate asset.
245
250
  def nameplate_url(nameplate_asset, format = 'webm')
246
251
  "#{cdn_url}/assets/collectibles/#{nameplate_asset.delete_suffix('/')}/asset.#{format}"
data/lib/discordrb/bot.rb CHANGED
@@ -887,12 +887,16 @@ module Discordrb
887
887
  # @param command_id [Integer, String]
888
888
  # @param server_id [Integer, String]
889
889
  # @param permissions [Array<Hash>] An array of objects formatted as `{ id: ENTITY_ID, type: 1 or 2, permission: true or false }`
890
- def edit_application_command_permissions(command_id, server_id, permissions = [])
890
+ # @param bearer_token [String] A valid bearer token that has permission to manage the server and its roles.
891
+ def edit_application_command_permissions(command_id, server_id, permissions = [], bearer_token = nil)
891
892
  builder = Interactions::PermissionBuilder.new
892
893
  yield builder if block_given?
893
894
 
895
+ raise ArgumentError, 'This method requires a valid bearer token to be provided' unless bearer_token
896
+
894
897
  permissions += builder.to_a
895
- API::Application.edit_guild_command_permissions(@token, profile.id, server_id, command_id, permissions)
898
+ bearer_token = "Bearer #{bearer_token.delete_prefix('Bearer ')}"
899
+ API::Application.edit_guild_command_permissions(bearer_token, profile.id, server_id, command_id, permissions)
896
900
  end
897
901
 
898
902
  # Fetches all the application emojis that the bot can use.
@@ -1059,6 +1063,9 @@ module Discordrb
1059
1063
  channel = data.is_a?(Discordrb::Channel) ? data : Channel.new(data, self)
1060
1064
  server = channel.server
1061
1065
 
1066
+ # The last message ID of a forum channel is the most recent post
1067
+ channel.parent.process_last_message_id(channel.id) if channel.parent&.forum? || channel.parent&.media?
1068
+
1062
1069
  # Handle normal and private channels separately
1063
1070
  if server
1064
1071
  server.add_channel(channel)
@@ -1358,11 +1365,20 @@ module Discordrb
1358
1365
  raise_event(ChannelCreateEvent.new(message.channel, self))
1359
1366
  end
1360
1367
 
1368
+ message.channel.process_last_message_id(message.id)
1369
+
1361
1370
  event = MessageEvent.new(message, self)
1362
1371
  raise_event(event)
1363
1372
 
1364
- if message.mentions.any? { |user| user.id == @profile.id }
1365
- event = MentionEvent.new(message, self)
1373
+ # Raise a mention event for any direct mentions.
1374
+ if message.mentions.any? { |user| user.id == profile.id }
1375
+ event = MentionEvent.new(message, self, false)
1376
+ raise_event(event)
1377
+ end
1378
+
1379
+ # Raise a mention event for the current bot's auto-generated role.
1380
+ if message.role_mentions.any? { |role| role.tags&.bot_id == profile.id }
1381
+ event = MentionEvent.new(message, self, true)
1366
1382
  raise_event(event)
1367
1383
  end
1368
1384
 
@@ -1452,6 +1468,10 @@ module Discordrb
1452
1468
 
1453
1469
  event = ReactionRemoveAllEvent.new(data, self)
1454
1470
  raise_event(event)
1471
+ when :MESSAGE_REACTION_REMOVE_EMOJI
1472
+
1473
+ event = ReactionRemoveEmojiEvent.new(data, self)
1474
+ raise_event(event)
1455
1475
  when :PRESENCE_UPDATE
1456
1476
  # Ignore friends list presences
1457
1477
  return unless data['guild_id']
@@ -138,6 +138,7 @@ module Discordrb
138
138
  # @option attributes [String, Integer, User] :from Matches the user who added the reaction.
139
139
  # @option attributes [String, Integer, Message] :message Matches the message to which the reaction was added.
140
140
  # @option attributes [String, Integer, Channel] :in Matches the channel the reaction was added in.
141
+ # @option attributes [Integer, String, Symbol] :type Matches the type of reaction (`:normal` or `:burst`) that was added.
141
142
  # @yield The block is executed when the event is raised.
142
143
  # @yieldparam event [ReactionAddEvent] The event that was raised.
143
144
  # @return [ReactionAddEventHandler] The event handler that was registered.
@@ -152,6 +153,7 @@ module Discordrb
152
153
  # @option attributes [String, Integer, User] :from Matches the user who removed the reaction.
153
154
  # @option attributes [String, Integer, Message] :message Matches the message to which the reaction was removed.
154
155
  # @option attributes [String, Integer, Channel] :in Matches the channel the reaction was removed in.
156
+ # @option attributes [Integer, String, Symbol] :type Matches the type of reaction (`:normal` or `:burst`) that was added.
155
157
  # @yield The block is executed when the event is raised.
156
158
  # @yieldparam event [ReactionRemoveEvent] The event that was raised.
157
159
  # @return [ReactionRemoveEventHandler] The event handler that was registered.
@@ -171,6 +173,18 @@ module Discordrb
171
173
  register_event(ReactionRemoveAllEvent, attributes, block)
172
174
  end
173
175
 
176
+ # This **event** is raised when somebody removes all instances of a single reaction from a message.
177
+ # @param attributes [Hash] The event's attributes.
178
+ # @option attributes [String, Integer, Message] :message Matches the message where the reaction was removed.
179
+ # @option attributes [String, Integer, Channel] :in Matches the channel where the reaction was removed.
180
+ # @option attributes [String, Integer] :emoji Matches the ID of the emoji that was removed, or its name.
181
+ # @yield The block is executed when the event is raised.
182
+ # @yieldparam event [ReactionRemoveEmojiEvent] The event that was raised.
183
+ # @return [ReactionRemoveEmojiEventHandler] The event handler that was registered.
184
+ def reaction_remove_emoji(attributes = {}, &block)
185
+ register_event(ReactionRemoveEmojiEvent, attributes, block)
186
+ end
187
+
174
188
  # This **event** is raised when a user's status (online/offline/idle) changes.
175
189
  # @param attributes [Hash] The event's attributes.
176
190
  # @option attributes [String, Integer, User] :from Matches the user whose status changed.
@@ -208,6 +222,7 @@ module Discordrb
208
222
  # @option attributes [Time] :before Matches a time before the time the message was sent at.
209
223
  # @option attributes [Boolean] :private Matches whether or not the channel is private.
210
224
  # @option attributes [Integer, String, Symbol] :type Matches the type of the message that was sent.
225
+ # @option attributes [true, false] :role_mention If the event should trigger when the bot's managed role is mentioned.
211
226
  # @yield The block is executed when the event is raised.
212
227
  # @yieldparam event [MentionEvent] The event that was raised.
213
228
  # @return [MentionEventHandler] the event handler that was registered.
@@ -22,7 +22,8 @@ module Discordrb
22
22
  private_thread: 12,
23
23
  stage_voice: 13,
24
24
  directory: 14,
25
- forum: 15
25
+ forum: 15,
26
+ media: 16
26
27
  }.freeze
27
28
 
28
29
  # @return [String] this channel's name.
@@ -98,6 +99,23 @@ module Discordrb
98
99
  # @return [Time, nil] The time at when the last pinned message was pinned in this channel.
99
100
  attr_reader :last_pin_timestamp
100
101
 
102
+ # @return [Integer, nil] The ID of the last message sent in this channel. This may not point to a valid message.
103
+ attr_reader :last_message_id
104
+
105
+ # @return [Integer] An approximate count of messages sent in this thread, including deleted messages.
106
+ attr_reader :total_message_sent
107
+ alias_method :total_messages_sent, :total_message_sent
108
+
109
+ # @return [Integer] The flags set on this channel combined as a bitfield.
110
+ attr_reader :flags
111
+
112
+ # @return [String, nil] The ID of the RTC voice region for this voice or stage channel. A region of `nil` means the
113
+ # the voice region will automatically be determined by Discord.
114
+ attr_reader :voice_region
115
+
116
+ # @return [Integer, nil] The video quality mode of this voice or stage channel.
117
+ attr_reader :video_quality_mode
118
+
101
119
  # @return [true, false] whether or not this channel is a PM or group channel.
102
120
  def private?
103
121
  pm? || group?
@@ -149,6 +167,7 @@ module Discordrb
149
167
  @rate_limit_per_user = data['rate_limit_per_user'] || 0
150
168
  @message_count = data['message_count']
151
169
  @member_count = data['member_count']
170
+ @total_message_sent = data['total_message_sent'] || 0
152
171
 
153
172
  if (metadata = data['thread_metadata'])
154
173
  @archived = metadata['archived']
@@ -163,6 +182,11 @@ module Discordrb
163
182
  @member_flags = member['flags']
164
183
  end
165
184
 
185
+ @flags = data['flags'] || 0
186
+ @voice_region = data['rtc_region']
187
+ @video_quality_mode = data['video_quality_mode']
188
+ @last_message_id = data['last_message_id']&.to_i
189
+
166
190
  process_last_pin_timestamp(data['last_pin_timestamp'])
167
191
  process_permission_overwrites(data['permission_overwrites'])
168
192
  end
@@ -235,6 +259,26 @@ module Discordrb
235
259
  news_thread? || public_thread? || private_thread?
236
260
  end
237
261
 
262
+ # @return [true, false] whether or not this channel is a stage channel.
263
+ def stage?
264
+ @type == 13
265
+ end
266
+
267
+ # @return [true, false] whether or not this channel is a directory channel.
268
+ def directory?
269
+ @type == 14
270
+ end
271
+
272
+ # @return [true, false] whether or not this channel is a forum channel.
273
+ def forum?
274
+ @type == 15
275
+ end
276
+
277
+ # @return [true, false] whether or not this channel is a media channel.
278
+ def media?
279
+ @type == 16
280
+ end
281
+
238
282
  # @return [Channel, nil] the category channel, if this channel is in a category
239
283
  def category
240
284
  @bot.channel(@parent_id) if @parent_id
@@ -665,6 +709,11 @@ module Discordrb
665
709
  @invitable = other.invitable?
666
710
  @message_count = other.message_count
667
711
  @last_pin_timestamp = other.last_pin_timestamp
712
+ @last_message_id = other.last_message_id
713
+ @total_message_sent = other.total_message_sent
714
+ @flags = other.flags
715
+ @voice_region = other.voice_region
716
+ @video_quality_mode = other.video_quality_mode
668
717
  end
669
718
 
670
719
  # The list of users currently in this channel. For a voice channel, it will return all the members currently
@@ -914,6 +963,19 @@ module Discordrb
914
963
  invites.map { |invite_data| Invite.new(invite_data, @bot) }
915
964
  end
916
965
 
966
+ # Returns the last message or forum post created in this channel.
967
+ # @return [Message, Channel, nil] the last message sent in this channel,
968
+ # the most recent forum post if this is a forum or media channel, or `nil`.
969
+ def last_message
970
+ return unless @last_message_id
971
+
972
+ if forum? || media?
973
+ @bot.channel(@last_message_id)
974
+ else
975
+ load_message(@last_message_id)
976
+ end
977
+ end
978
+
917
979
  # Start a thread.
918
980
  # @param name [String] The name of the thread.
919
981
  # @param auto_archive_duration [60, 1440, 4320, 10080] How long before a thread is automatically
@@ -1038,6 +1100,14 @@ module Discordrb
1038
1100
  @last_pin_timestamp = time ? Time.parse(time) : time
1039
1101
  end
1040
1102
 
1103
+ # Set the last message ID of a channel.
1104
+ # @param id [Integer, nil] the ID of the last message in a channel
1105
+ # @note For internal use only
1106
+ # @!visibility private
1107
+ def process_last_message_id(id)
1108
+ @last_message_id = id
1109
+ end
1110
+
1041
1111
  # Updates the cached data with new data
1042
1112
  # @note For internal use only
1043
1113
  # @!visibility private
@@ -1057,7 +1127,7 @@ module Discordrb
1057
1127
 
1058
1128
  # @return [String] a URL that a user can use to navigate to this channel in the client
1059
1129
  def link
1060
- "https://discord.com/channels/#{@server_id || '@me'}/#{@channel.id}"
1130
+ "https://discord.com/channels/#{@server_id || '@me'}/#{@id}"
1061
1131
  end
1062
1132
 
1063
1133
  alias_method :jump_link, :link
@@ -36,9 +36,11 @@ module Discordrb
36
36
  end
37
37
 
38
38
  # Utility method to get the URL of this nameplate.
39
- # @return [String] CDN url of this nameplate.
40
- def url
41
- API.nameplate_url(@asset)
39
+ # @param static [true, false] Whether to return the static URL of this
40
+ # nameplate instead of the animated URL.
41
+ # @return [String] The CDN url of this nameplate.
42
+ def url(static: false)
43
+ static ? API.static_nameplate_url(@asset) : API.nameplate_url(@asset)
42
44
  end
43
45
  end
44
46
  end
@@ -53,7 +53,7 @@ module Discordrb
53
53
  # ID or name based comparison
54
54
  def ==(other)
55
55
  return false unless other.is_a? Emoji
56
- return Discordrb.id_compare(@id, other) if @id
56
+ return Discordrb.id_compare?(@id, other) if @id
57
57
 
58
58
  name == other.name
59
59
  end
@@ -5,6 +5,8 @@ require 'discordrb/webhooks'
5
5
  module Discordrb
6
6
  # Base class for interaction objects.
7
7
  class Interaction
8
+ include IDObject
9
+
8
10
  # Interaction types.
9
11
  # @see https://discord.com/developers/docs/interactions/slash-commands#interaction-interactiontype
10
12
  TYPES = {
@@ -36,8 +38,8 @@ module Discordrb
36
38
  # @return [Integer] The ID of the channel this interaction originates from.
37
39
  attr_reader :channel_id
38
40
 
39
- # @return [Integer] The ID of this interaction.
40
- attr_reader :id
41
+ # @return [Channel] The channel where this interaction originates from.
42
+ attr_reader :channel
41
43
 
42
44
  # @return [Integer] The ID of the application associated with this interaction.
43
45
  attr_reader :application_id
@@ -56,9 +58,27 @@ module Discordrb
56
58
  # @return [Hash] The interaction data.
57
59
  attr_reader :data
58
60
 
59
- # @return [Array<ActionRow>]
61
+ # @return [Array<ActionRow>] The modal components associated with this interaction.
60
62
  attr_reader :components
61
63
 
64
+ # @return [Permissions] The permissions the application has where this interaction originates from.
65
+ attr_reader :application_permissions
66
+
67
+ # @return [String] The selected language of the user that initiated this interaction.
68
+ attr_reader :user_locale
69
+
70
+ # @return [String, nil] The selected language of the server this interaction originates from.
71
+ attr_reader :server_locale
72
+
73
+ # @return [Integer] The context of where this interaction was initiated from.
74
+ attr_reader :context
75
+
76
+ # @return [Integer] The maximum number of bytes an attachment can have when responding to this interaction.
77
+ attr_reader :max_attachment_size
78
+
79
+ # @return [Array<Symbol>] the features of the server where the interaction was initiated from.
80
+ attr_reader :server_features
81
+
62
82
  # @!visibility private
63
83
  def initialize(data, bot)
64
84
  @bot = bot
@@ -70,6 +90,7 @@ module Discordrb
70
90
  @data = data['data']
71
91
  @server_id = data['guild_id']&.to_i
72
92
  @channel_id = data['channel_id']&.to_i
93
+ @channel = bot.ensure_channel(data['channel']) if data['channel']
73
94
  @user = if data['member']
74
95
  data['member']['guild_id'] = @server_id
75
96
  Discordrb::Member.new(data['member'], bot.servers[@server_id], bot)
@@ -79,6 +100,13 @@ module Discordrb
79
100
  @token = data['token']
80
101
  @version = data['version']
81
102
  @components = @data['components']&.map { |component| Components.from_data(component, @bot) }&.compact || []
103
+ @application_permissions = Permissions.new(data['app_permissions']) if data['app_permissions']
104
+ @user_locale = data['locale']
105
+ @server_locale = data['guild_locale']
106
+ @context = data['context']
107
+ @max_attachment_size = data['attachment_size_limit']
108
+ @integration_owners = data['authorizing_integration_owners']&.to_h { |key, value| [key.to_i, value.to_i] }
109
+ @server_features = data['guild'] ? data['guild']['features']&.map { |feature| feature.downcase.to_sym } : []
82
110
  end
83
111
 
84
112
  # Respond to the creation of this interaction. An interaction must be responded to or deferred,
@@ -279,12 +307,6 @@ module Discordrb
279
307
  @bot.server(@server_id)
280
308
  end
281
309
 
282
- # @return [Channel, nil]
283
- # @raise [Errors::NoPermission] When the bot is not in the server associated with this interaction.
284
- def channel
285
- @bot.channel(@channel_id)
286
- end
287
-
288
310
  # @return [Hash, nil] Returns the button that triggered this interaction if applicable, otherwise nil
289
311
  def button
290
312
  return unless @type == TYPES[:component]
@@ -309,6 +331,16 @@ module Discordrb
309
331
  components.find { |component| component.custom_id == custom_id }
310
332
  end
311
333
 
334
+ # @return [true, false] whether the application was installed by the user who initiated this interaction.
335
+ def user_integration?
336
+ @integration_owners[1] == @user_id
337
+ end
338
+
339
+ # @return [true, false] whether the application was installed by the server where this interaction originates from.
340
+ def server_integration?
341
+ @server_id ? @integration_owners[0] == @server_id : false
342
+ end
343
+
312
344
  private
313
345
 
314
346
  # Set builder defaults from parameters