discordrb 3.4.0 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +419 -222
- 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 +182 -11
- data/lib/discordrb/api/interaction.rb +54 -0
- data/lib/discordrb/api/invite.rb +2 -2
- data/lib/discordrb/api/server.rb +42 -19
- data/lib/discordrb/api/user.rb +9 -3
- data/lib/discordrb/api/webhook.rb +57 -0
- data/lib/discordrb/api.rb +19 -5
- data/lib/discordrb/bot.rb +328 -33
- data/lib/discordrb/cache.rb +27 -22
- data/lib/discordrb/commands/command_bot.rb +14 -7
- 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/activity.rb +8 -1
- 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 +2 -2
- data/lib/discordrb/data/member.rb +108 -33
- data/lib/discordrb/data/message.rb +100 -20
- 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 +13 -4
- 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 +53 -19
- data/.codeclimate.yml +0 -16
- data/.travis.yml +0 -32
- data/bin/travis_build_docs.sh +0 -17
@@ -87,7 +87,7 @@ module Discordrb
|
|
87
87
|
def initialize(data, bot)
|
88
88
|
@bot = bot
|
89
89
|
|
90
|
-
@channel = if data['channel_id']
|
90
|
+
@channel = if data['channel_id']
|
91
91
|
bot.channel(data['channel_id'])
|
92
92
|
else
|
93
93
|
InviteChannel.new(data['channel'], bot)
|
@@ -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']
|
@@ -83,14 +87,16 @@ module Discordrb
|
|
83
87
|
@referenced_message = Message.new(data['referenced_message'], bot) if data['referenced_message']
|
84
88
|
@message_reference = data['message_reference']
|
85
89
|
|
86
|
-
@server =
|
90
|
+
@server = @channel.server
|
91
|
+
|
92
|
+
@webhook_id = data['webhook_id']&.to_i
|
87
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
|
}
|