discordrb 3.4.3 → 3.5.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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +44 -18
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -1
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -1
- data/.github/workflows/codeql.yml +65 -0
- data/.markdownlint.json +4 -0
- data/.rubocop.yml +8 -2
- data/CHANGELOG.md +390 -225
- data/LICENSE.txt +1 -1
- data/README.md +37 -25
- data/discordrb-webhooks.gemspec +4 -1
- data/discordrb.gemspec +9 -6
- data/lib/discordrb/api/application.rb +202 -0
- data/lib/discordrb/api/channel.rb +177 -11
- data/lib/discordrb/api/interaction.rb +54 -0
- data/lib/discordrb/api/invite.rb +2 -2
- data/lib/discordrb/api/server.rb +40 -19
- data/lib/discordrb/api/user.rb +8 -3
- data/lib/discordrb/api/webhook.rb +57 -0
- data/lib/discordrb/api.rb +19 -5
- data/lib/discordrb/bot.rb +317 -32
- data/lib/discordrb/cache.rb +27 -22
- data/lib/discordrb/commands/command_bot.rb +6 -4
- data/lib/discordrb/commands/container.rb +1 -1
- data/lib/discordrb/commands/parser.rb +2 -2
- data/lib/discordrb/commands/rate_limiter.rb +1 -1
- data/lib/discordrb/container.rb +132 -3
- data/lib/discordrb/data/attachment.rb +15 -0
- data/lib/discordrb/data/audit_logs.rb +3 -3
- data/lib/discordrb/data/channel.rb +167 -23
- data/lib/discordrb/data/component.rb +229 -0
- data/lib/discordrb/data/integration.rb +42 -3
- data/lib/discordrb/data/interaction.rb +800 -0
- data/lib/discordrb/data/invite.rb +1 -1
- data/lib/discordrb/data/member.rb +108 -33
- data/lib/discordrb/data/message.rb +99 -19
- data/lib/discordrb/data/overwrite.rb +13 -7
- data/lib/discordrb/data/role.rb +58 -1
- data/lib/discordrb/data/server.rb +82 -80
- data/lib/discordrb/data/user.rb +69 -9
- data/lib/discordrb/data/webhook.rb +97 -4
- data/lib/discordrb/data.rb +3 -0
- data/lib/discordrb/errors.rb +44 -3
- data/lib/discordrb/events/channels.rb +1 -1
- data/lib/discordrb/events/interactions.rb +482 -0
- data/lib/discordrb/events/message.rb +9 -6
- data/lib/discordrb/events/presence.rb +21 -14
- data/lib/discordrb/events/reactions.rb +0 -1
- data/lib/discordrb/events/threads.rb +96 -0
- data/lib/discordrb/gateway.rb +30 -17
- data/lib/discordrb/permissions.rb +59 -34
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +2 -2
- data/lib/discordrb/voice/network.rb +18 -7
- data/lib/discordrb/voice/sodium.rb +3 -1
- data/lib/discordrb/voice/voice_bot.rb +3 -3
- data/lib/discordrb/webhooks.rb +2 -0
- data/lib/discordrb.rb +37 -4
- metadata +48 -14
- data/.codeclimate.yml +0 -16
- data/.travis.yml +0 -32
- data/bin/travis_build_docs.sh +0 -17
@@ -100,7 +100,7 @@ module Discordrb
|
|
100
100
|
end
|
101
101
|
|
102
102
|
@uses = data['uses']
|
103
|
-
@inviter = data['inviter'] ?
|
103
|
+
@inviter = data['inviter'] ? bot.ensure_user(data['inviter']) : nil
|
104
104
|
@temporary = data['temporary']
|
105
105
|
@revoked = data['revoked']
|
106
106
|
@online_member_count = data['approximate_presence_count']
|
@@ -18,6 +18,10 @@ module Discordrb
|
|
18
18
|
|
19
19
|
# @return [Server] the server this member is on.
|
20
20
|
attr_reader :server
|
21
|
+
|
22
|
+
# @return [Time] When the user's timeout will expire.
|
23
|
+
attr_reader :communication_disabled_until
|
24
|
+
alias_method :timeout, :communication_disabled_until
|
21
25
|
end
|
22
26
|
|
23
27
|
# A member is a user on a server. It differs from regular users in that it has roles, voice statuses and things like
|
@@ -62,17 +66,39 @@ module Discordrb
|
|
62
66
|
@user = bot.ensure_user(data['user'])
|
63
67
|
super @user # Initialize the delegate class
|
64
68
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
@server = server || bot.server(data['guild_id'].to_i)
|
69
|
+
@server = server
|
70
|
+
@server_id = server&.id || data['guild_id'].to_i
|
69
71
|
|
70
|
-
|
71
|
-
update_roles(data['roles'])
|
72
|
+
@role_ids = data['roles']&.map(&:to_i) || []
|
72
73
|
|
73
74
|
@nick = data['nick']
|
74
75
|
@joined_at = data['joined_at'] ? Time.parse(data['joined_at']) : nil
|
75
76
|
@boosting_since = data['premium_since'] ? Time.parse(data['premium_since']) : nil
|
77
|
+
timeout_until = data['communication_disabled_until']
|
78
|
+
@communication_disabled_until = timeout_until ? Time.parse(timeout_until) : nil
|
79
|
+
@permissions = Permissions.new(data['permissions']) if data['permissions']
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Server] the server this member is on.
|
83
|
+
# @raise [Discordrb::Errors::NoPermission] This can happen when receiving interactions for servers in which the bot is not
|
84
|
+
# authorized with the `bot` scope.
|
85
|
+
def server
|
86
|
+
return @server if @server
|
87
|
+
|
88
|
+
@server = @bot.server(@server_id)
|
89
|
+
raise Discordrb::Errors::NoPermission, 'The bot does not have access to this server' unless @server
|
90
|
+
|
91
|
+
@server
|
92
|
+
end
|
93
|
+
|
94
|
+
# @return [Array<Role>] the roles this member has.
|
95
|
+
# @raise [Discordrb::Errors::NoPermission] This can happen when receiving interactions for servers in which the bot is not
|
96
|
+
# authorized with the `bot` scope.
|
97
|
+
def roles
|
98
|
+
return @roles if @roles
|
99
|
+
|
100
|
+
update_roles(@role_ids)
|
101
|
+
@roles
|
76
102
|
end
|
77
103
|
|
78
104
|
# @return [true, false] if this user is a Nitro Booster of this server.
|
@@ -82,14 +108,14 @@ module Discordrb
|
|
82
108
|
|
83
109
|
# @return [true, false] whether this member is the server owner.
|
84
110
|
def owner?
|
85
|
-
|
111
|
+
server.owner == self
|
86
112
|
end
|
87
113
|
|
88
114
|
# @param role [Role, String, Integer] the role to check or its ID.
|
89
115
|
# @return [true, false] whether this member has the specified role.
|
90
116
|
def role?(role)
|
91
117
|
role = role.resolve_id
|
92
|
-
|
118
|
+
roles.any?(role)
|
93
119
|
end
|
94
120
|
|
95
121
|
# @see Member#set_roles
|
@@ -97,12 +123,30 @@ module Discordrb
|
|
97
123
|
set_roles(role)
|
98
124
|
end
|
99
125
|
|
126
|
+
# Check if the current user has communication disabled.
|
127
|
+
# @return [true, false]
|
128
|
+
def communication_disabled?
|
129
|
+
!@communication_disabled_until.nil? && @communication_disabled_until > Time.now
|
130
|
+
end
|
131
|
+
|
132
|
+
alias_method :timeout?, :communication_disabled?
|
133
|
+
|
134
|
+
# Set a user's timeout duration, or remove it by setting the timeout to `nil`.
|
135
|
+
# @param timeout_until [Time, nil] When the timeout will end.
|
136
|
+
def communication_disabled_until=(timeout_until)
|
137
|
+
raise ArgumentError, 'A time out cannot exceed 28 days' if timeout_until && timeout_until > (Time.now + 2_419_200)
|
138
|
+
|
139
|
+
API::Server.update_member(@bot.token, @server_id, @user.id, communication_disabled_until: timeout_until.iso8601)
|
140
|
+
end
|
141
|
+
|
142
|
+
alias_method :timeout=, :communication_disabled_until=
|
143
|
+
|
100
144
|
# Bulk sets a member's roles.
|
101
145
|
# @param role [Role, Array<Role>] The role(s) to set.
|
102
146
|
# @param reason [String] The reason the user's roles are being changed.
|
103
147
|
def set_roles(role, reason = nil)
|
104
148
|
role_ids = role_id_array(role)
|
105
|
-
API::Server.update_member(@bot.token, @
|
149
|
+
API::Server.update_member(@bot.token, @server_id, @user.id, roles: role_ids, reason: reason)
|
106
150
|
end
|
107
151
|
|
108
152
|
# Adds and removes roles from a member.
|
@@ -116,10 +160,10 @@ module Discordrb
|
|
116
160
|
def modify_roles(add, remove, reason = nil)
|
117
161
|
add_role_ids = role_id_array(add)
|
118
162
|
remove_role_ids = role_id_array(remove)
|
119
|
-
old_role_ids =
|
163
|
+
old_role_ids = resolve_role_ids
|
120
164
|
new_role_ids = (old_role_ids - remove_role_ids + add_role_ids).uniq
|
121
165
|
|
122
|
-
API::Server.update_member(@bot.token, @
|
166
|
+
API::Server.update_member(@bot.token, @server_id, @user.id, roles: new_role_ids, reason: reason)
|
123
167
|
end
|
124
168
|
|
125
169
|
# Adds one or more roles to this member.
|
@@ -129,11 +173,11 @@ module Discordrb
|
|
129
173
|
role_ids = role_id_array(role)
|
130
174
|
|
131
175
|
if role_ids.count == 1
|
132
|
-
API::Server.add_member_role(@bot.token, @
|
176
|
+
API::Server.add_member_role(@bot.token, @server_id, @user.id, role_ids[0], reason)
|
133
177
|
else
|
134
|
-
old_role_ids =
|
178
|
+
old_role_ids = resolve_role_ids
|
135
179
|
new_role_ids = (old_role_ids + role_ids).uniq
|
136
|
-
API::Server.update_member(@bot.token, @
|
180
|
+
API::Server.update_member(@bot.token, @server_id, @user.id, roles: new_role_ids, reason: reason)
|
137
181
|
end
|
138
182
|
end
|
139
183
|
|
@@ -144,22 +188,22 @@ module Discordrb
|
|
144
188
|
role_ids = role_id_array(role)
|
145
189
|
|
146
190
|
if role_ids.count == 1
|
147
|
-
API::Server.remove_member_role(@bot.token, @
|
191
|
+
API::Server.remove_member_role(@bot.token, @server_id, @user.id, role_ids[0], reason)
|
148
192
|
else
|
149
|
-
old_role_ids =
|
193
|
+
old_role_ids = resolve_role_ids
|
150
194
|
new_role_ids = old_role_ids.reject { |i| role_ids.include?(i) }
|
151
|
-
API::Server.update_member(@bot.token, @
|
195
|
+
API::Server.update_member(@bot.token, @server_id, @user.id, roles: new_role_ids, reason: reason)
|
152
196
|
end
|
153
197
|
end
|
154
198
|
|
155
199
|
# @return [Role] the highest role this member has.
|
156
200
|
def highest_role
|
157
|
-
|
201
|
+
roles.max_by(&:position)
|
158
202
|
end
|
159
203
|
|
160
204
|
# @return [Role, nil] the role this member is being hoisted with.
|
161
205
|
def hoist_role
|
162
|
-
hoisted_roles =
|
206
|
+
hoisted_roles = roles.select(&:hoist)
|
163
207
|
return nil if hoisted_roles.empty?
|
164
208
|
|
165
209
|
hoisted_roles.max_by(&:position)
|
@@ -167,7 +211,7 @@ module Discordrb
|
|
167
211
|
|
168
212
|
# @return [Role, nil] the role this member is basing their colour on.
|
169
213
|
def colour_role
|
170
|
-
coloured_roles =
|
214
|
+
coloured_roles = roles.select { |v| v.colour.combined.nonzero? }
|
171
215
|
return nil if coloured_roles.empty?
|
172
216
|
|
173
217
|
coloured_roles.max_by(&:position)
|
@@ -184,22 +228,41 @@ module Discordrb
|
|
184
228
|
|
185
229
|
# Server deafens this member.
|
186
230
|
def server_deafen
|
187
|
-
API::Server.update_member(@bot.token, @
|
231
|
+
API::Server.update_member(@bot.token, @server_id, @user.id, deaf: true)
|
188
232
|
end
|
189
233
|
|
190
234
|
# Server undeafens this member.
|
191
235
|
def server_undeafen
|
192
|
-
API::Server.update_member(@bot.token, @
|
236
|
+
API::Server.update_member(@bot.token, @server_id, @user.id, deaf: false)
|
193
237
|
end
|
194
238
|
|
195
239
|
# Server mutes this member.
|
196
240
|
def server_mute
|
197
|
-
API::Server.update_member(@bot.token, @
|
241
|
+
API::Server.update_member(@bot.token, @server_id, @user.id, mute: true)
|
198
242
|
end
|
199
243
|
|
200
244
|
# Server unmutes this member.
|
201
245
|
def server_unmute
|
202
|
-
API::Server.update_member(@bot.token, @
|
246
|
+
API::Server.update_member(@bot.token, @server_id, @user.id, mute: false)
|
247
|
+
end
|
248
|
+
|
249
|
+
# Bans this member from the server.
|
250
|
+
# @param message_days [Integer] How many days worth of messages sent by the member should be deleted.
|
251
|
+
# @param reason [String] The reason this member is being banned.
|
252
|
+
def ban(message_days = 0, reason: nil)
|
253
|
+
server.ban(@user, message_days, reason: reason)
|
254
|
+
end
|
255
|
+
|
256
|
+
# Unbans this member from the server.
|
257
|
+
# @param reason [String] The reason this member is being unbanned.
|
258
|
+
def unban(reason = nil)
|
259
|
+
server.unban(@user, reason)
|
260
|
+
end
|
261
|
+
|
262
|
+
# Kicks this member from the server.
|
263
|
+
# @param reason [String] The reason this member is being kicked.
|
264
|
+
def kick(reason = nil)
|
265
|
+
server.kick(@user, reason)
|
203
266
|
end
|
204
267
|
|
205
268
|
# @see Member#set_nick
|
@@ -218,28 +281,28 @@ module Discordrb
|
|
218
281
|
nick ||= ''
|
219
282
|
|
220
283
|
if @user.current_bot?
|
221
|
-
API::User.change_own_nickname(@bot.token, @
|
284
|
+
API::User.change_own_nickname(@bot.token, @server_id, nick, reason)
|
222
285
|
else
|
223
|
-
API::Server.update_member(@bot.token, @
|
286
|
+
API::Server.update_member(@bot.token, @server_id, @user.id, nick: nick, reason: nil)
|
224
287
|
end
|
225
288
|
end
|
226
289
|
|
227
290
|
alias_method :set_nickname, :set_nick
|
228
291
|
|
229
|
-
# @return [String] the name the user displays as (nickname if they have one, username otherwise)
|
292
|
+
# @return [String] the name the user displays as (nickname if they have one, global_name if they have one, username otherwise)
|
230
293
|
def display_name
|
231
|
-
nickname || username
|
294
|
+
nickname || global_name || username
|
232
295
|
end
|
233
296
|
|
234
297
|
# Update this member's roles
|
235
298
|
# @note For internal use only.
|
236
299
|
# @!visibility private
|
237
300
|
def update_roles(role_ids)
|
238
|
-
@roles = [
|
301
|
+
@roles = [server.role(@server_id)]
|
239
302
|
role_ids.each do |id|
|
240
303
|
# It is possible for members to have roles that do not exist
|
241
|
-
# on the server any longer. See https://github.com/
|
242
|
-
role =
|
304
|
+
# on the server any longer. See https://github.com/discordrb/discordrb/issues/371
|
305
|
+
role = server.role(id)
|
243
306
|
@roles << role if role
|
244
307
|
end
|
245
308
|
end
|
@@ -258,6 +321,12 @@ module Discordrb
|
|
258
321
|
@boosting_since = time
|
259
322
|
end
|
260
323
|
|
324
|
+
# @!visibility private
|
325
|
+
def update_communication_disabled_until(time)
|
326
|
+
time = time ? Time.parse(time) : nil
|
327
|
+
@communication_disabled_until = time
|
328
|
+
end
|
329
|
+
|
261
330
|
# Update this member
|
262
331
|
# @note For internal use only.
|
263
332
|
# @!visibility private
|
@@ -268,13 +337,15 @@ module Discordrb
|
|
268
337
|
@deaf = data['deaf'] if data.key?('deaf')
|
269
338
|
|
270
339
|
@joined_at = Time.parse(data['joined_at']) if data['joined_at']
|
340
|
+
timeout_until = data['communication_disabled_until']
|
341
|
+
@communication_disabled_until = timeout_until ? Time.parse(timeout_until) : nil
|
271
342
|
end
|
272
343
|
|
273
344
|
include PermissionCalculator
|
274
345
|
|
275
346
|
# Overwriting inspect for debug purposes
|
276
347
|
def inspect
|
277
|
-
"<Member user=#{@user.inspect} server=#{@server
|
348
|
+
"<Member user=#{@user.inspect} server=#{@server&.inspect || @server_id} joined_at=#{@joined_at} roles=#{@roles&.inspect || @role_ids} voice_channel=#{@voice_channel.inspect} mute=#{@mute} deaf=#{@deaf} self_mute=#{@self_mute} self_deaf=#{@self_deaf}>"
|
278
349
|
end
|
279
350
|
|
280
351
|
private
|
@@ -290,8 +361,12 @@ module Discordrb
|
|
290
361
|
|
291
362
|
# Utility method to get data out of this member's voice state
|
292
363
|
def voice_state_attribute(name)
|
293
|
-
voice_state =
|
364
|
+
voice_state = server.voice_states[@user.id]
|
294
365
|
voice_state&.send name
|
295
366
|
end
|
367
|
+
|
368
|
+
def resolve_role_ids
|
369
|
+
@roles ? @roles.collect(&:id) : @role_ids
|
370
|
+
end
|
296
371
|
end
|
297
372
|
end
|
@@ -61,14 +61,17 @@ module Discordrb
|
|
61
61
|
attr_reader :pinned
|
62
62
|
alias_method :pinned?, :pinned
|
63
63
|
|
64
|
+
# @return [Integer] what the type of the message is
|
65
|
+
attr_reader :type
|
66
|
+
|
64
67
|
# @return [Server, nil] the server in which this message was sent.
|
65
68
|
attr_reader :server
|
66
69
|
|
67
70
|
# @return [Integer, nil] the webhook ID that sent this message, or `nil` if it wasn't sent through a webhook.
|
68
71
|
attr_reader :webhook_id
|
69
72
|
|
70
|
-
#
|
71
|
-
|
73
|
+
# @return [Array<Component>]
|
74
|
+
attr_reader :components
|
72
75
|
|
73
76
|
# @!visibility private
|
74
77
|
def initialize(data, bot)
|
@@ -76,6 +79,7 @@ module Discordrb
|
|
76
79
|
@content = data['content']
|
77
80
|
@channel = bot.channel(data['channel_id'].to_i)
|
78
81
|
@pinned = data['pinned']
|
82
|
+
@type = data['type']
|
79
83
|
@tts = data['tts']
|
80
84
|
@nonce = data['nonce']
|
81
85
|
@mention_everyone = data['mention_everyone']
|
@@ -85,12 +89,14 @@ module Discordrb
|
|
85
89
|
|
86
90
|
@server = @channel.server
|
87
91
|
|
92
|
+
@webhook_id = data['webhook_id']&.to_i
|
93
|
+
|
88
94
|
@author = if data['author']
|
89
|
-
if
|
95
|
+
if @webhook_id
|
90
96
|
# This is a webhook user! It would be pointless to try to resolve a member here, so we just create
|
91
97
|
# a User and return that instead.
|
92
98
|
Discordrb::LOGGER.debug("Webhook user: #{data['author']['id']}")
|
93
|
-
User.new(data['author'], @bot)
|
99
|
+
User.new(data['author'].merge({ '_webhook' => true }), @bot)
|
94
100
|
elsif @channel.private?
|
95
101
|
# Turn the message user into a recipient - we can't use the channel recipient
|
96
102
|
# directly because the bot may also send messages to the channel
|
@@ -100,11 +106,12 @@ module Discordrb
|
|
100
106
|
|
101
107
|
if member
|
102
108
|
member.update_data(data['member']) if data['member']
|
109
|
+
member.update_global_name(data['author']['global_name']) if data['author']['global_name']
|
103
110
|
else
|
104
111
|
Discordrb::LOGGER.debug("Member with ID #{data['author']['id']} not cached (possibly left the server).")
|
105
112
|
member = if data['member']
|
106
113
|
member_data = data['author'].merge(data['member'])
|
107
|
-
Member.new(member_data, bot)
|
114
|
+
Member.new(member_data, @server, bot)
|
108
115
|
else
|
109
116
|
@bot.ensure_user(data['author'])
|
110
117
|
end
|
@@ -114,8 +121,6 @@ module Discordrb
|
|
114
121
|
end
|
115
122
|
end
|
116
123
|
|
117
|
-
@webhook_id = data['webhook_id'].to_i if data['webhook_id']
|
118
|
-
|
119
124
|
@timestamp = Time.parse(data['timestamp']) if data['timestamp']
|
120
125
|
@edited_timestamp = data['edited_timestamp'].nil? ? nil : Time.parse(data['edited_timestamp'])
|
121
126
|
@edited = !@edited_timestamp.nil?
|
@@ -149,43 +154,53 @@ module Discordrb
|
|
149
154
|
|
150
155
|
@embeds = []
|
151
156
|
@embeds = data['embeds'].map { |e| Embed.new(e, self) } if data['embeds']
|
157
|
+
|
158
|
+
@components = []
|
159
|
+
@components = data['components'].map { |component_data| Components.from_data(component_data, @bot) } if data['components']
|
152
160
|
end
|
153
161
|
|
154
162
|
# Replies to this message with the specified content.
|
155
163
|
# @deprecated Please use {#respond}.
|
164
|
+
# @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
|
165
|
+
# @return (see #respond)
|
156
166
|
# @see Channel#send_message
|
157
167
|
def reply(content)
|
158
168
|
@channel.send_message(content)
|
159
169
|
end
|
160
170
|
|
161
|
-
#
|
171
|
+
# Responds to this message as an inline reply.
|
162
172
|
# @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
|
163
173
|
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
|
164
174
|
# @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
|
165
175
|
# @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
|
166
176
|
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
|
167
177
|
# @param mention_user [true, false] Whether the user that is being replied to should be pinged by the reply.
|
168
|
-
# @
|
169
|
-
|
178
|
+
# @param components [View, Array<Hash>] Interaction components to associate with this message.
|
179
|
+
# @return (see #respond)
|
180
|
+
def reply!(content, tts: false, embed: nil, attachments: nil, allowed_mentions: {}, mention_user: false, components: nil)
|
170
181
|
allowed_mentions = { parse: [] } if allowed_mentions == false
|
171
182
|
allowed_mentions = allowed_mentions.to_hash.transform_keys(&:to_sym)
|
172
183
|
allowed_mentions[:replied_user] = mention_user
|
173
184
|
|
174
|
-
respond(content, tts, embed, attachments, allowed_mentions, self)
|
185
|
+
respond(content, tts, embed, attachments, allowed_mentions, self, components)
|
175
186
|
end
|
176
187
|
|
177
188
|
# (see Channel#send_message)
|
178
|
-
def respond(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil)
|
179
|
-
@channel.send_message(content, tts, embed, attachments, allowed_mentions, message_reference)
|
189
|
+
def respond(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
|
190
|
+
@channel.send_message(content, tts, embed, attachments, allowed_mentions, message_reference, components)
|
180
191
|
end
|
181
192
|
|
182
193
|
# Edits this message to have the specified content instead.
|
183
194
|
# You can only edit your own messages.
|
184
195
|
# @param new_content [String] the new content the message should have.
|
185
|
-
# @param
|
196
|
+
# @param new_embeds [Hash, Discordrb::Webhooks::Embed, Array<Hash>, Array<Discordrb::Webhooks::Embed>, nil] The new embeds the message should have. If `nil` the message will be changed to have no embeds.
|
197
|
+
# @param new_components [View, Array<Hash>] The new components the message should have. If `nil` the message will be changed to have no components.
|
186
198
|
# @return [Message] the resulting message.
|
187
|
-
def edit(new_content,
|
188
|
-
|
199
|
+
def edit(new_content, new_embeds = nil, new_components = nil)
|
200
|
+
new_embeds = (new_embeds.instance_of?(Array) ? new_embeds.map(&:to_hash) : [new_embeds&.to_hash]).compact
|
201
|
+
new_components = new_components&.to_a || []
|
202
|
+
|
203
|
+
response = API::Channel.edit_message(@bot.token, @channel.id, @id, new_content, [], new_embeds, new_components)
|
189
204
|
Message.new(JSON.parse(response), @bot)
|
190
205
|
end
|
191
206
|
|
@@ -222,6 +237,19 @@ module Discordrb
|
|
222
237
|
@bot.add_await!(Discordrb::Events::MessageEvent, { from: @author.id, in: @channel.id }.merge(attributes), &block)
|
223
238
|
end
|
224
239
|
|
240
|
+
# Add an {Await} for a reaction to be added on this message.
|
241
|
+
# @see Bot#add_await
|
242
|
+
# @deprecated Will be changed to blocking behavior in v4.0. Use {#await_reaction!} instead.
|
243
|
+
def await_reaction(key, attributes = {}, &block)
|
244
|
+
@bot.add_await(key, Discordrb::Events::ReactionAddEvent, { message: @id }.merge(attributes), &block)
|
245
|
+
end
|
246
|
+
|
247
|
+
# Add a blocking {Await} for a reaction to be added on this message.
|
248
|
+
# @see Bot#add_await!
|
249
|
+
def await_reaction!(attributes = {}, &block)
|
250
|
+
@bot.add_await!(Discordrb::Events::ReactionAddEvent, { message: @id }.merge(attributes), &block)
|
251
|
+
end
|
252
|
+
|
225
253
|
# @return [true, false] whether this message was sent by the current {Bot}.
|
226
254
|
def from_bot?
|
227
255
|
@author&.current_bot?
|
@@ -276,14 +304,38 @@ module Discordrb
|
|
276
304
|
# @return [Array<User>] the users who used this reaction
|
277
305
|
def reacted_with(reaction, limit: 100)
|
278
306
|
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
307
|
+
reaction = reaction.to_s if reaction.respond_to?(:to_s)
|
308
|
+
|
309
|
+
get_reactions = proc do |fetch_limit, after_id = nil|
|
310
|
+
resp = API::Channel.get_reactions(@bot.token, @channel.id, @id, reaction, nil, after_id, fetch_limit)
|
311
|
+
return JSON.parse(resp).map { |d| User.new(d, @bot) }
|
312
|
+
end
|
313
|
+
|
314
|
+
# Can be done without pagination
|
315
|
+
return get_reactions.call(limit) if limit && limit <= 100
|
316
|
+
|
279
317
|
paginator = Paginator.new(limit, :down) do |last_page|
|
280
|
-
|
281
|
-
|
282
|
-
|
318
|
+
if last_page && last_page.count < 100
|
319
|
+
[]
|
320
|
+
else
|
321
|
+
get_reactions.call(100, last_page&.last&.id)
|
322
|
+
end
|
283
323
|
end
|
324
|
+
|
284
325
|
paginator.to_a
|
285
326
|
end
|
286
327
|
|
328
|
+
# Returns a hash of all reactions to a message as keys and the users that reacted to it as values.
|
329
|
+
# @param limit [Integer] the limit of how many users to retrieve per distinct reaction emoji. `nil` will return all users
|
330
|
+
# @example Get all the users that reacted to a message for a giveaway.
|
331
|
+
# giveaway_participants = message.all_reaction_users
|
332
|
+
# @return [Hash<String => Array<User>>] A hash mapping the string representation of a
|
333
|
+
# reaction to an array of users.
|
334
|
+
def all_reaction_users(limit: 100)
|
335
|
+
all_reactions = @reactions.map { |r| { r.to_s => reacted_with(r, limit: limit) } }
|
336
|
+
all_reactions.reduce({}, :merge)
|
337
|
+
end
|
338
|
+
|
287
339
|
# Deletes a reaction made by a user on this message.
|
288
340
|
# @param user [User, String, Integer] the user or user ID who used this reaction
|
289
341
|
# @param reaction [String, #to_reaction] the reaction to remove
|
@@ -322,6 +374,12 @@ module Discordrb
|
|
322
374
|
!@referenced_message.nil?
|
323
375
|
end
|
324
376
|
|
377
|
+
# Whether or not this message was of type "CHAT_INPUT_COMMAND"
|
378
|
+
# @return [true, false]
|
379
|
+
def chat_input_command?
|
380
|
+
@type == 20
|
381
|
+
end
|
382
|
+
|
325
383
|
# @return [Message, nil] the Message this Message was sent in reply to.
|
326
384
|
def referenced_message
|
327
385
|
return @referenced_message if @referenced_message
|
@@ -330,5 +388,27 @@ module Discordrb
|
|
330
388
|
referenced_channel = @bot.channel(@message_reference['channel_id'])
|
331
389
|
@referenced_message = referenced_channel.message(@message_reference['message_id'])
|
332
390
|
end
|
391
|
+
|
392
|
+
# @return [Array<Components::Button>]
|
393
|
+
def buttons
|
394
|
+
results = @components.collect do |component|
|
395
|
+
case component
|
396
|
+
when Components::Button
|
397
|
+
component
|
398
|
+
when Components::ActionRow
|
399
|
+
component.buttons
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
results.flatten.compact
|
404
|
+
end
|
405
|
+
|
406
|
+
# to_message -> self or message
|
407
|
+
# @return [Discordrb::Message]
|
408
|
+
def to_message
|
409
|
+
self
|
410
|
+
end
|
411
|
+
|
412
|
+
alias_method :message, :to_message
|
333
413
|
end
|
334
414
|
end
|
@@ -4,6 +4,12 @@ module Discordrb
|
|
4
4
|
# A permissions overwrite, when applied to channels describes additional
|
5
5
|
# permissions a member needs to perform certain actions in context.
|
6
6
|
class Overwrite
|
7
|
+
# Types of overwrites mapped to their API value.
|
8
|
+
TYPES = {
|
9
|
+
role: 0,
|
10
|
+
member: 1
|
11
|
+
}.freeze
|
12
|
+
|
7
13
|
# @return [Integer] ID of the thing associated with this overwrite type
|
8
14
|
attr_accessor :id
|
9
15
|
|
@@ -32,14 +38,14 @@ module Discordrb
|
|
32
38
|
# @example Create an overwrite by ID and permissions bits
|
33
39
|
# Overwrite.new(120571255635181568, type: 'member', allow: 1024, deny: 0)
|
34
40
|
# @param object [Integer, #id] the ID or object this overwrite is for
|
35
|
-
# @param type [String] the type of object this overwrite is for (only required if object is an Integer)
|
36
|
-
# @param allow [Integer, Permissions] allowed permissions for this overwrite, by bits or a Permissions object
|
37
|
-
# @param deny [Integer, Permissions] denied permissions for this overwrite, by bits or a Permissions object
|
41
|
+
# @param type [String, Symbol, Integer] the type of object this overwrite is for (only required if object is an Integer)
|
42
|
+
# @param allow [String, Integer, Permissions] allowed permissions for this overwrite, by bits or a Permissions object
|
43
|
+
# @param deny [String, Integer, Permissions] denied permissions for this overwrite, by bits or a Permissions object
|
38
44
|
# @raise [ArgumentError] if type is not :member or :role
|
39
45
|
def initialize(object = nil, type: nil, allow: 0, deny: 0)
|
40
46
|
if type
|
41
|
-
type = type.to_sym
|
42
|
-
raise ArgumentError, 'Overwrite type must be :member or :role' unless
|
47
|
+
type = TYPES.value?(type) ? TYPES.key(type) : type.to_sym
|
48
|
+
raise ArgumentError, 'Overwrite type must be :member or :role' unless type
|
43
49
|
end
|
44
50
|
|
45
51
|
@id = object.respond_to?(:id) ? object.id : object
|
@@ -71,7 +77,7 @@ module Discordrb
|
|
71
77
|
def self.from_hash(data)
|
72
78
|
new(
|
73
79
|
data['id'].to_i,
|
74
|
-
type: data['type'],
|
80
|
+
type: TYPES.key(data['type']),
|
75
81
|
allow: Permissions.new(data['allow']),
|
76
82
|
deny: Permissions.new(data['deny'])
|
77
83
|
)
|
@@ -93,7 +99,7 @@ module Discordrb
|
|
93
99
|
def to_hash
|
94
100
|
{
|
95
101
|
id: id,
|
96
|
-
type: type,
|
102
|
+
type: TYPES[type],
|
97
103
|
allow: allow.bits,
|
98
104
|
deny: deny.bits
|
99
105
|
}
|
data/lib/discordrb/data/role.rb
CHANGED
@@ -32,6 +32,42 @@ module Discordrb
|
|
32
32
|
# @return [Integer] the position of this role in the hierarchy
|
33
33
|
attr_reader :position
|
34
34
|
|
35
|
+
# @return [String, nil] The icon hash for this role.
|
36
|
+
attr_reader :icon
|
37
|
+
|
38
|
+
# @return [Tags, nil] The role tags
|
39
|
+
attr_reader :tags
|
40
|
+
|
41
|
+
# Wrapper for the role tags
|
42
|
+
class Tags
|
43
|
+
# @return [Integer, nil] The ID of the bot this role belongs to
|
44
|
+
attr_reader :bot_id
|
45
|
+
|
46
|
+
# @return [Integer, nil] The ID of the integration this role belongs to
|
47
|
+
attr_reader :integration_id
|
48
|
+
|
49
|
+
# @return [true, false] Whether this is the guild's Booster role
|
50
|
+
attr_reader :premium_subscriber
|
51
|
+
|
52
|
+
# @return [Integer, nil] The id of this role's subscription sku and listing
|
53
|
+
attr_reader :subscription_listing_id
|
54
|
+
|
55
|
+
# @return [true, false] Whether this role is available for purchase
|
56
|
+
attr_reader :available_for_purchase
|
57
|
+
|
58
|
+
# @return [true, false] Whether this role is a guild's linked role
|
59
|
+
attr_reader :guild_connections
|
60
|
+
|
61
|
+
def initialize(data)
|
62
|
+
@bot_id = data['bot_id']&.resolve_id
|
63
|
+
@integration_id = data['integration_id']&.resolve_id
|
64
|
+
@premium_subscriber = data.key?('premium_subscriber')
|
65
|
+
@subscription_listing_id = data['subscription_listing_id']&.resolve_id
|
66
|
+
@available_for_purchase = data.key?('available_for_purchase')
|
67
|
+
@guild_connections = data.key?('guild_connections')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
35
71
|
# This class is used internally as a wrapper to a Role object that allows easy writing of permission data.
|
36
72
|
class RoleWriter
|
37
73
|
# @!visibility private
|
@@ -67,6 +103,10 @@ module Discordrb
|
|
67
103
|
@managed = data['managed']
|
68
104
|
|
69
105
|
@colour = ColourRGB.new(data['color'])
|
106
|
+
|
107
|
+
@icon = data['icon']
|
108
|
+
|
109
|
+
@tags = Tags.new(data['tags']) if data['tags']
|
70
110
|
end
|
71
111
|
|
72
112
|
# @return [String] a string that will mention this role, if it is mentionable.
|
@@ -92,6 +132,7 @@ module Discordrb
|
|
92
132
|
@colour = other.colour
|
93
133
|
@position = other.position
|
94
134
|
@managed = other.managed
|
135
|
+
@icon = other.icon
|
95
136
|
end
|
96
137
|
|
97
138
|
# Updates the data cache from a hash containing data
|
@@ -128,6 +169,20 @@ module Discordrb
|
|
128
169
|
update_role_data(colour: colour)
|
129
170
|
end
|
130
171
|
|
172
|
+
# Upload a role icon for servers with the ROLE_ICONS feature.
|
173
|
+
# @param file [File]
|
174
|
+
def icon=(file)
|
175
|
+
update_role_data(icon: file)
|
176
|
+
end
|
177
|
+
|
178
|
+
# @param format ['webp', 'png', 'jpeg']
|
179
|
+
# @return [String] URL to the icon on Discord's CDN.
|
180
|
+
def icon_url(format = 'webp')
|
181
|
+
return nil unless @icon
|
182
|
+
|
183
|
+
Discordrb::API.role_icon_url(@id, @icon, format)
|
184
|
+
end
|
185
|
+
|
131
186
|
alias_method :color=, :colour=
|
132
187
|
|
133
188
|
# Changes this role's permissions to a fixed bitfield. This allows setting multiple permissions at once with just
|
@@ -184,7 +239,9 @@ module Discordrb
|
|
184
239
|
(new_data[:colour] || @colour).combined,
|
185
240
|
new_data[:hoist].nil? ? @hoist : new_data[:hoist],
|
186
241
|
new_data[:mentionable].nil? ? @mentionable : new_data[:mentionable],
|
187
|
-
new_data[:permissions] || @permissions.bits
|
242
|
+
new_data[:permissions] || @permissions.bits,
|
243
|
+
nil,
|
244
|
+
new_data.key?(:icon) ? new_data[:icon] : :undef)
|
188
245
|
update_data(new_data)
|
189
246
|
end
|
190
247
|
end
|