discordrb 3.3.0 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of discordrb might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.circleci/config.yml +126 -0
- data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +0 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- data/.github/pull_request_template.md +37 -0
- data/.rubocop.yml +34 -37
- data/.travis.yml +5 -6
- data/CHANGELOG.md +472 -347
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +61 -79
- data/Rakefile +2 -0
- data/bin/console +1 -0
- data/discordrb-webhooks.gemspec +6 -6
- data/discordrb.gemspec +17 -17
- data/lib/discordrb.rb +73 -0
- data/lib/discordrb/allowed_mentions.rb +36 -0
- data/lib/discordrb/api.rb +40 -15
- data/lib/discordrb/api/channel.rb +57 -39
- data/lib/discordrb/api/invite.rb +3 -3
- data/lib/discordrb/api/server.rb +55 -50
- data/lib/discordrb/api/user.rb +8 -8
- data/lib/discordrb/api/webhook.rb +6 -6
- data/lib/discordrb/await.rb +0 -1
- data/lib/discordrb/bot.rb +164 -72
- data/lib/discordrb/cache.rb +4 -2
- data/lib/discordrb/colour_rgb.rb +43 -0
- data/lib/discordrb/commands/command_bot.rb +22 -6
- data/lib/discordrb/commands/container.rb +20 -23
- data/lib/discordrb/commands/parser.rb +18 -18
- data/lib/discordrb/commands/rate_limiter.rb +3 -2
- data/lib/discordrb/container.rb +77 -17
- data/lib/discordrb/data.rb +25 -4180
- data/lib/discordrb/data/activity.rb +264 -0
- data/lib/discordrb/data/application.rb +50 -0
- data/lib/discordrb/data/attachment.rb +56 -0
- data/lib/discordrb/data/audit_logs.rb +345 -0
- data/lib/discordrb/data/channel.rb +849 -0
- data/lib/discordrb/data/embed.rb +251 -0
- data/lib/discordrb/data/emoji.rb +82 -0
- data/lib/discordrb/data/integration.rb +83 -0
- data/lib/discordrb/data/invite.rb +137 -0
- data/lib/discordrb/data/member.rb +297 -0
- data/lib/discordrb/data/message.rb +334 -0
- data/lib/discordrb/data/overwrite.rb +102 -0
- data/lib/discordrb/data/profile.rb +91 -0
- data/lib/discordrb/data/reaction.rb +33 -0
- data/lib/discordrb/data/recipient.rb +34 -0
- data/lib/discordrb/data/role.rb +191 -0
- data/lib/discordrb/data/server.rb +1002 -0
- data/lib/discordrb/data/user.rb +204 -0
- data/lib/discordrb/data/voice_region.rb +45 -0
- data/lib/discordrb/data/voice_state.rb +41 -0
- data/lib/discordrb/data/webhook.rb +145 -0
- data/lib/discordrb/errors.rb +2 -1
- data/lib/discordrb/events/bans.rb +7 -5
- data/lib/discordrb/events/channels.rb +2 -0
- data/lib/discordrb/events/guilds.rb +16 -9
- data/lib/discordrb/events/invites.rb +125 -0
- data/lib/discordrb/events/members.rb +6 -2
- data/lib/discordrb/events/message.rb +69 -27
- data/lib/discordrb/events/presence.rb +14 -4
- data/lib/discordrb/events/raw.rb +1 -3
- data/lib/discordrb/events/reactions.rb +49 -3
- data/lib/discordrb/events/typing.rb +6 -4
- data/lib/discordrb/events/voice_server_update.rb +47 -0
- data/lib/discordrb/events/voice_state_update.rb +15 -10
- data/lib/discordrb/events/webhooks.rb +9 -6
- data/lib/discordrb/gateway.rb +72 -57
- data/lib/discordrb/id_object.rb +39 -0
- data/lib/discordrb/light/integrations.rb +1 -1
- data/lib/discordrb/light/light_bot.rb +1 -1
- data/lib/discordrb/logger.rb +4 -4
- data/lib/discordrb/paginator.rb +57 -0
- data/lib/discordrb/permissions.rb +103 -8
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +3 -3
- data/lib/discordrb/voice/network.rb +84 -43
- data/lib/discordrb/voice/sodium.rb +96 -0
- data/lib/discordrb/voice/voice_bot.rb +34 -26
- metadata +93 -55
data/lib/discordrb/api/user.rb
CHANGED
@@ -5,7 +5,7 @@ module Discordrb::API::User
|
|
5
5
|
module_function
|
6
6
|
|
7
7
|
# Get user data
|
8
|
-
# https://
|
8
|
+
# https://discord.com/developers/docs/resources/user#get-user
|
9
9
|
def resolve(token, user_id)
|
10
10
|
Discordrb::API.request(
|
11
11
|
:users_uid,
|
@@ -17,7 +17,7 @@ module Discordrb::API::User
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# Get profile data
|
20
|
-
# https://
|
20
|
+
# https://discord.com/developers/docs/resources/user#get-current-user
|
21
21
|
def profile(token)
|
22
22
|
Discordrb::API.request(
|
23
23
|
:users_me,
|
@@ -43,7 +43,7 @@ module Discordrb::API::User
|
|
43
43
|
end
|
44
44
|
|
45
45
|
# Update user data
|
46
|
-
# https://
|
46
|
+
# https://discord.com/developers/docs/resources/user#modify-current-user
|
47
47
|
def update_profile(token, email, password, new_username, avatar, new_password = nil)
|
48
48
|
Discordrb::API.request(
|
49
49
|
:users_me,
|
@@ -57,7 +57,7 @@ module Discordrb::API::User
|
|
57
57
|
end
|
58
58
|
|
59
59
|
# Get the servers a user is connected to
|
60
|
-
# https://
|
60
|
+
# https://discord.com/developers/docs/resources/user#get-current-user-guilds
|
61
61
|
def servers(token)
|
62
62
|
Discordrb::API.request(
|
63
63
|
:users_me_guilds,
|
@@ -69,7 +69,7 @@ module Discordrb::API::User
|
|
69
69
|
end
|
70
70
|
|
71
71
|
# Leave a server
|
72
|
-
# https://
|
72
|
+
# https://discord.com/developers/docs/resources/user#leave-guild
|
73
73
|
def leave_server(token, server_id)
|
74
74
|
Discordrb::API.request(
|
75
75
|
:users_me_guilds_sid,
|
@@ -81,7 +81,7 @@ module Discordrb::API::User
|
|
81
81
|
end
|
82
82
|
|
83
83
|
# Get the DMs for the current user
|
84
|
-
# https://
|
84
|
+
# https://discord.com/developers/docs/resources/user#get-user-dms
|
85
85
|
def user_dms(token)
|
86
86
|
Discordrb::API.request(
|
87
87
|
:users_me_channels,
|
@@ -93,7 +93,7 @@ module Discordrb::API::User
|
|
93
93
|
end
|
94
94
|
|
95
95
|
# Create a DM to another user
|
96
|
-
# https://
|
96
|
+
# https://discord.com/developers/docs/resources/user#create-dm
|
97
97
|
def create_pm(token, recipient_id)
|
98
98
|
Discordrb::API.request(
|
99
99
|
:users_me_channels,
|
@@ -107,7 +107,7 @@ module Discordrb::API::User
|
|
107
107
|
end
|
108
108
|
|
109
109
|
# Get information about a user's connections
|
110
|
-
# https://
|
110
|
+
# https://discord.com/developers/docs/resources/user#get-users-connections
|
111
111
|
def connections(token)
|
112
112
|
Discordrb::API.request(
|
113
113
|
:users_me_connections,
|
@@ -5,7 +5,7 @@ module Discordrb::API::Webhook
|
|
5
5
|
module_function
|
6
6
|
|
7
7
|
# Get a webhook
|
8
|
-
# https://
|
8
|
+
# https://discord.com/developers/docs/resources/webhook#get-webhook
|
9
9
|
def webhook(token, webhook_id)
|
10
10
|
Discordrb::API.request(
|
11
11
|
:webhooks_wid,
|
@@ -17,7 +17,7 @@ module Discordrb::API::Webhook
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# Get a webhook via webhook token
|
20
|
-
# https://
|
20
|
+
# https://discord.com/developers/docs/resources/webhook#get-webhook-with-token
|
21
21
|
def token_webhook(webhook_token, webhook_id)
|
22
22
|
Discordrb::API.request(
|
23
23
|
:webhooks_wid,
|
@@ -28,7 +28,7 @@ module Discordrb::API::Webhook
|
|
28
28
|
end
|
29
29
|
|
30
30
|
# Update a webhook
|
31
|
-
# https://
|
31
|
+
# https://discord.com/developers/docs/resources/webhook#modify-webhook
|
32
32
|
def update_webhook(token, webhook_id, data, reason = nil)
|
33
33
|
Discordrb::API.request(
|
34
34
|
:webhooks_wid,
|
@@ -43,7 +43,7 @@ module Discordrb::API::Webhook
|
|
43
43
|
end
|
44
44
|
|
45
45
|
# Update a webhook via webhook token
|
46
|
-
# https://
|
46
|
+
# https://discord.com/developers/docs/resources/webhook#modify-webhook-with-token
|
47
47
|
def token_update_webhook(webhook_token, webhook_id, data, reason = nil)
|
48
48
|
Discordrb::API.request(
|
49
49
|
:webhooks_wid,
|
@@ -57,7 +57,7 @@ module Discordrb::API::Webhook
|
|
57
57
|
end
|
58
58
|
|
59
59
|
# Deletes a webhook
|
60
|
-
# https://
|
60
|
+
# https://discord.com/developers/docs/resources/webhook#delete-webhook
|
61
61
|
def delete_webhook(token, webhook_id, reason = nil)
|
62
62
|
Discordrb::API.request(
|
63
63
|
:webhooks_wid,
|
@@ -70,7 +70,7 @@ module Discordrb::API::Webhook
|
|
70
70
|
end
|
71
71
|
|
72
72
|
# Deletes a webhook via webhook token
|
73
|
-
# https://
|
73
|
+
# https://discord.com/developers/docs/resources/webhook#delete-webhook-with-token
|
74
74
|
def token_delete_webhook(webhook_token, webhook_id, reason = nil)
|
75
75
|
Discordrb::API.request(
|
76
76
|
:webhooks_wid,
|
data/lib/discordrb/await.rb
CHANGED
@@ -43,7 +43,6 @@ module Discordrb
|
|
43
43
|
dummy_handler = EventContainer.handler_class(@type).new(@attributes, @bot)
|
44
44
|
return [nil, nil] unless event.instance_of?(@type) && dummy_handler.matches?(event)
|
45
45
|
|
46
|
-
should_delete = nil
|
47
46
|
should_delete = true if (@block && @block.call(event) != false) || !@block
|
48
47
|
|
49
48
|
[@key, should_delete]
|
data/lib/discordrb/bot.rb
CHANGED
@@ -18,6 +18,7 @@ require 'discordrb/events/bans'
|
|
18
18
|
require 'discordrb/events/raw'
|
19
19
|
require 'discordrb/events/reactions'
|
20
20
|
require 'discordrb/events/webhooks'
|
21
|
+
require 'discordrb/events/invites'
|
21
22
|
|
22
23
|
require 'discordrb/api'
|
23
24
|
require 'discordrb/api/channel'
|
@@ -101,12 +102,14 @@ module Discordrb
|
|
101
102
|
# to Discord's gateway. `:none` will request that no payloads are received compressed (not recommended for
|
102
103
|
# production bots). `:large` will request that large payloads are received compressed. `:stream` will request
|
103
104
|
# that all data be received in a continuous compressed stream.
|
105
|
+
# @param intents [:all, Array<Symbol>, nil] Intents that this bot requires. See {Discordrb::INTENTS}. If `nil`, no intents
|
106
|
+
# field will be passed.
|
104
107
|
def initialize(
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
108
|
+
log_mode: :normal,
|
109
|
+
token: nil, client_id: nil,
|
110
|
+
type: nil, name: '', fancy_log: false, suppress_ready: false, parse_self: false,
|
111
|
+
shard_id: nil, num_shards: nil, redact_token: true, ignore_bots: false,
|
112
|
+
compress_mode: :large, intents: nil
|
110
113
|
)
|
111
114
|
LOGGER.mode = log_mode
|
112
115
|
LOGGER.token = token if redact_token
|
@@ -125,8 +128,12 @@ module Discordrb
|
|
125
128
|
|
126
129
|
@compress_mode = compress_mode
|
127
130
|
|
131
|
+
raise 'Token string is empty or nil' if token.nil? || token.empty?
|
132
|
+
|
133
|
+
@intents = intents == :all ? INTENTS.values.reduce(&:|) : calculate_intents(intents) if intents
|
134
|
+
|
128
135
|
@token = process_token(@type, token)
|
129
|
-
@gateway = Gateway.new(self, @token, @shard_key, @compress_mode)
|
136
|
+
@gateway = Gateway.new(self, @token, @shard_key, @compress_mode, @intents)
|
130
137
|
|
131
138
|
init_cache
|
132
139
|
|
@@ -160,16 +167,13 @@ module Discordrb
|
|
160
167
|
|
161
168
|
# @overload emoji(id)
|
162
169
|
# Return an emoji by its ID
|
163
|
-
# @param id [
|
170
|
+
# @param id [String, Integer] The emoji's ID.
|
164
171
|
# @return [Emoji, nil] the emoji object. `nil` if the emoji was not found.
|
165
172
|
# @overload emoji
|
166
173
|
# The list of emoji the bot can use.
|
167
174
|
# @return [Array<Emoji>] the emoji available.
|
168
175
|
def emoji(id = nil)
|
169
|
-
|
170
|
-
unavailable_servers_check
|
171
|
-
|
172
|
-
emoji_hash = @servers.values.map(&:emoji).reduce(&:merge)
|
176
|
+
emoji_hash = servers.values.map(&:emoji).reduce(&:merge)
|
173
177
|
if id
|
174
178
|
id = id.resolve_id
|
175
179
|
emoji_hash[id]
|
@@ -203,6 +207,7 @@ module Discordrb
|
|
203
207
|
# @return [Application, nil] The bot's application info. Returns `nil` if bot is not a bot account.
|
204
208
|
def bot_application
|
205
209
|
return unless @type == :bot
|
210
|
+
|
206
211
|
response = API.oauth_application(token)
|
207
212
|
Application.new(JSON.parse(response), self)
|
208
213
|
end
|
@@ -225,7 +230,7 @@ module Discordrb
|
|
225
230
|
|
226
231
|
# Runs the bot, which logs into Discord and connects the WebSocket. This
|
227
232
|
# prevents all further execution unless it is executed with
|
228
|
-
# `
|
233
|
+
# `background` = `true`.
|
229
234
|
# @param background [true, false] If it is `true`, then the bot will run in
|
230
235
|
# another thread to allow further execution. If it is `false`, this method
|
231
236
|
# will block until {#stop} is called. If the bot is run with `true`, make
|
@@ -254,9 +259,9 @@ module Discordrb
|
|
254
259
|
|
255
260
|
# Stops the bot gracefully, disconnecting the websocket without immediately killing the thread. This means that
|
256
261
|
# Discord is immediately aware of the closed connection and makes the bot appear offline instantly.
|
257
|
-
# @
|
258
|
-
def stop(
|
259
|
-
@gateway.stop
|
262
|
+
# @note This method no longer takes an argument as of 3.4.0
|
263
|
+
def stop(_no_sync = nil)
|
264
|
+
@gateway.stop
|
260
265
|
end
|
261
266
|
|
262
267
|
# @return [true, false] whether or not the bot is currently connected to Discord.
|
@@ -273,14 +278,14 @@ module Discordrb
|
|
273
278
|
|
274
279
|
# Creates an OAuth invite URL that can be used to invite this bot to a particular server.
|
275
280
|
# @param server [Server, nil] The server the bot should be invited to, or nil if a general invite should be created.
|
276
|
-
# @param permission_bits [
|
281
|
+
# @param permission_bits [String, Integer] Permission bits that should be appended to invite url.
|
277
282
|
# @return [String] the OAuth invite URL.
|
278
283
|
def invite_url(server: nil, permission_bits: nil)
|
279
284
|
@client_id ||= bot_application.id
|
280
285
|
|
281
286
|
server_id_str = server ? "&guild_id=#{server.id}" : ''
|
282
287
|
permission_bits_str = permission_bits ? "&permissions=#{permission_bits}" : ''
|
283
|
-
"https://
|
288
|
+
"https://discord.com/oauth2/authorize?&client_id=#{@client_id}#{server_id_str}#{permission_bits_str}&scope=bot"
|
284
289
|
end
|
285
290
|
|
286
291
|
# @return [Hash<Integer => VoiceBot>] the voice connections this bot currently has, by the server ID to which they are connected.
|
@@ -299,22 +304,21 @@ module Discordrb
|
|
299
304
|
|
300
305
|
server_id = channel.server.id
|
301
306
|
return @voices[server_id] if @voices[server_id]
|
302
|
-
|
303
|
-
nil
|
304
307
|
end
|
305
308
|
|
306
309
|
# Connects to a voice channel, initializes network connections and returns the {Voice::VoiceBot} over which audio
|
307
310
|
# data can then be sent. After connecting, the bot can also be accessed using {#voice}. If the bot is already
|
308
311
|
# connected to voice, the existing connection will be terminated - you don't have to call
|
309
312
|
# {Discordrb::Voice::VoiceBot#destroy} before calling this method.
|
310
|
-
# @param chan [Channel,
|
311
|
-
# @param encrypted [true, false] Whether voice communication should be encrypted using
|
313
|
+
# @param chan [Channel, String, Integer] The voice channel, or its ID, to connect to.
|
314
|
+
# @param encrypted [true, false] Whether voice communication should be encrypted using
|
312
315
|
# (uses an XSalsa20 stream cipher for encryption and Poly1305 for authentication)
|
313
316
|
# @return [Voice::VoiceBot] the initialized bot over which audio data can then be sent.
|
314
317
|
def voice_connect(chan, encrypted = true)
|
318
|
+
raise ArgumentError, 'Unencrypted voice connections are no longer supported.' unless encrypted
|
319
|
+
|
315
320
|
chan = channel(chan.resolve_id)
|
316
321
|
server_id = chan.server.id
|
317
|
-
@should_encrypt_voice = encrypted
|
318
322
|
|
319
323
|
if @voices[chan.id]
|
320
324
|
debug('Voice bot exists already! Destroying it')
|
@@ -336,7 +340,7 @@ module Discordrb
|
|
336
340
|
|
337
341
|
# Disconnects the client from a specific voice connection given the server ID. Usually it's more convenient to use
|
338
342
|
# {Discordrb::Voice::VoiceBot#destroy} rather than this.
|
339
|
-
# @param server [Server,
|
343
|
+
# @param server [Server, String, Integer] The server, or server ID, the voice connection is on.
|
340
344
|
# @param destroy_vws [true, false] Whether or not the VWS should also be destroyed. If you're calling this method
|
341
345
|
# directly, you should leave it as true.
|
342
346
|
def voice_destroy(server, destroy_vws = true)
|
@@ -355,31 +359,37 @@ module Discordrb
|
|
355
359
|
end
|
356
360
|
|
357
361
|
# Sends a text message to a channel given its ID and the message's content.
|
358
|
-
# @param channel [Channel,
|
362
|
+
# @param channel [Channel, String, Integer] The channel, or its ID, to send something to.
|
359
363
|
# @param content [String] The text that should be sent as a message. It is limited to 2000 characters (Discord imposed).
|
360
364
|
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
|
361
365
|
# @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
|
366
|
+
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
|
367
|
+
# @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
|
362
368
|
# @return [Message] The message that was sent.
|
363
|
-
def send_message(channel, content, tts = false, embed = nil)
|
369
|
+
def send_message(channel, content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil)
|
364
370
|
channel = channel.resolve_id
|
365
371
|
debug("Sending message to #{channel} with content '#{content}'")
|
372
|
+
allowed_mentions = { parse: [] } if allowed_mentions == false
|
373
|
+
message_reference = { message_id: message_reference.id } if message_reference
|
366
374
|
|
367
|
-
response = API::Channel.create_message(token, channel, content, tts, embed
|
375
|
+
response = API::Channel.create_message(token, channel, content, tts, embed&.to_hash, nil, attachments, allowed_mentions&.to_hash, message_reference)
|
368
376
|
Message.new(JSON.parse(response), self)
|
369
377
|
end
|
370
378
|
|
371
379
|
# Sends a text message to a channel given its ID and the message's content,
|
372
380
|
# then deletes it after the specified timeout in seconds.
|
373
|
-
# @param channel [Channel,
|
381
|
+
# @param channel [Channel, String, Integer] The channel, or its ID, to send something to.
|
374
382
|
# @param content [String] The text that should be sent as a message. It is limited to 2000 characters (Discord imposed).
|
375
383
|
# @param timeout [Float] The amount of time in seconds after which the message sent will be deleted.
|
376
384
|
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
|
377
385
|
# @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
|
378
|
-
|
386
|
+
# @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
|
387
|
+
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
|
388
|
+
def send_temporary_message(channel, content, timeout, tts = false, embed = nil, attachments = nil, allowed_mentions = nil)
|
379
389
|
Thread.new do
|
380
390
|
Thread.current[:discordrb_name] = "#{@current_thread}-temp-msg"
|
381
391
|
|
382
|
-
message = send_message(channel, content, tts, embed)
|
392
|
+
message = send_message(channel, content, tts, embed, attachments, allowed_mentions)
|
383
393
|
sleep(timeout)
|
384
394
|
message.delete
|
385
395
|
end
|
@@ -389,13 +399,24 @@ module Discordrb
|
|
389
399
|
|
390
400
|
# Sends a file to a channel. If it is an image, it will automatically be embedded.
|
391
401
|
# @note This executes in a blocking way, so if you're sending long files, be wary of delays.
|
392
|
-
# @param channel [Channel,
|
402
|
+
# @param channel [Channel, String, Integer] The channel, or its ID, to send something to.
|
393
403
|
# @param file [File] The file that should be sent.
|
394
404
|
# @param caption [string] The caption for the file.
|
395
405
|
# @param tts [true, false] Whether or not this file's caption should be sent using Discord text-to-speech.
|
406
|
+
# @param filename [String] Overrides the filename of the uploaded file
|
407
|
+
# @param spoiler [true, false] Whether or not this file should appear as a spoiler.
|
396
408
|
# @example Send a file from disk
|
397
409
|
# bot.send_file(83281822225530880, File.open('rubytaco.png', 'r'))
|
398
|
-
def send_file(channel, file, caption: nil, tts: false)
|
410
|
+
def send_file(channel, file, caption: nil, tts: false, filename: nil, spoiler: nil)
|
411
|
+
if file.respond_to?(:read)
|
412
|
+
if spoiler
|
413
|
+
filename ||= File.basename(file.path)
|
414
|
+
filename = "SPOILER_#{filename}" unless filename.start_with? 'SPOILER_'
|
415
|
+
end
|
416
|
+
# https://github.com/rest-client/rest-client/blob/v2.0.2/lib/restclient/payload.rb#L160
|
417
|
+
file.define_singleton_method(:original_filename) { filename } if filename
|
418
|
+
end
|
419
|
+
|
399
420
|
channel = channel.resolve_id
|
400
421
|
response = API::Channel.upload_file(token, channel, file, caption: caption, tts: tts)
|
401
422
|
Message.new(JSON.parse(response), self)
|
@@ -410,14 +431,13 @@ module Discordrb
|
|
410
431
|
def create_server(name, region = :'eu-central')
|
411
432
|
response = API::Server.create(token, name, region)
|
412
433
|
id = JSON.parse(response)['id'].to_i
|
413
|
-
sleep 0.1 until @servers[id]
|
414
|
-
server = @servers[id]
|
434
|
+
sleep 0.1 until (server = @servers[id])
|
415
435
|
debug "Successfully created server #{server.id} with name #{server.name}"
|
416
436
|
server
|
417
437
|
end
|
418
438
|
|
419
439
|
# Creates a new application to do OAuth authorization with. This allows you to use OAuth to authorize users using
|
420
|
-
# Discord. For information how to use this, see the docs: https://
|
440
|
+
# Discord. For information how to use this, see the docs: https://discord.com/developers/docs/topics/oauth2
|
421
441
|
# @param name [String] What your application should be called.
|
422
442
|
# @param redirect_uris [Array<String>] URIs that Discord should redirect your users to after authorizing.
|
423
443
|
# @return [Array(String, String)] your applications' client ID and client secret to be used in OAuth authorization.
|
@@ -436,28 +456,46 @@ module Discordrb
|
|
436
456
|
API.update_oauth_application(@token, name, redirect_uris, description, icon)
|
437
457
|
end
|
438
458
|
|
439
|
-
# Gets the
|
459
|
+
# Gets the users, channels, roles and emoji from a string.
|
460
|
+
# @param mentions [String] The mentions, which should look like `<@12314873129>`, `<#123456789>`, `<@&123456789>` or `<:name:126328:>`.
|
461
|
+
# @param server [Server, nil] The server of the associated mentions. (recommended for role parsing, to speed things up)
|
462
|
+
# @return [Array<User, Channel, Role, Emoji>] The array of users, channels, roles and emoji identified by the mentions, or `nil` if none exists.
|
463
|
+
def parse_mentions(mentions, server = nil)
|
464
|
+
array_to_return = []
|
465
|
+
# While possible mentions may be in message
|
466
|
+
while mentions.include?('<') && mentions.include?('>')
|
467
|
+
# Removing all content before the next possible mention
|
468
|
+
mentions = mentions.split('<', 2)[1]
|
469
|
+
# Locate the first valid mention enclosed in `<...>`, otherwise advance to the next open `<`
|
470
|
+
next unless mentions.split('>', 2).first.length < mentions.split('<', 2).first.length
|
471
|
+
|
472
|
+
# Store the possible mention value to be validated with RegEx
|
473
|
+
mention = mentions.split('>', 2).first
|
474
|
+
if /@!?(?<id>\d+)/ =~ mention
|
475
|
+
array_to_return << user(id) unless user(id).nil?
|
476
|
+
elsif /#(?<id>\d+)/ =~ mention
|
477
|
+
array_to_return << channel(id, server) unless channel(id, server).nil?
|
478
|
+
elsif /@&(?<id>\d+)/ =~ mention
|
479
|
+
if server
|
480
|
+
array_to_return << server.role(id) unless server.role(id).nil?
|
481
|
+
else
|
482
|
+
@servers.each_value do |element|
|
483
|
+
array_to_return << element.role(id) unless element.role(id).nil?
|
484
|
+
end
|
485
|
+
end
|
486
|
+
elsif /(?<animated>^a|^${0}):(?<name>\w+):(?<id>\d+)/ =~ mention
|
487
|
+
array_to_return << (emoji(id) || Emoji.new({ 'animated' => !animated.nil?, 'name' => name, 'id' => id }, self, nil))
|
488
|
+
end
|
489
|
+
end
|
490
|
+
array_to_return
|
491
|
+
end
|
492
|
+
|
493
|
+
# Gets the user, channel, role or emoji from a string.
|
440
494
|
# @param mention [String] The mention, which should look like `<@12314873129>`, `<#123456789>`, `<@&123456789>` or `<:name:126328:>`.
|
441
495
|
# @param server [Server, nil] The server of the associated mention. (recommended for role parsing, to speed things up)
|
442
496
|
# @return [User, Channel, Role, Emoji] The user, channel, role or emoji identified by the mention, or `nil` if none exists.
|
443
497
|
def parse_mention(mention, server = nil)
|
444
|
-
|
445
|
-
if /<@!?(?<id>\d+)>/ =~ mention
|
446
|
-
user(id)
|
447
|
-
elsif /<#(?<id>\d+)>/ =~ mention
|
448
|
-
channel(id, server)
|
449
|
-
elsif /<@&(?<id>\d+)>/ =~ mention
|
450
|
-
return server.role(id) if server
|
451
|
-
@servers.values.each do |element|
|
452
|
-
role = element.role(id)
|
453
|
-
return role unless role.nil?
|
454
|
-
end
|
455
|
-
|
456
|
-
# Return nil if no role is found
|
457
|
-
nil
|
458
|
-
elsif /<(?<animated>a)?:(?<name>\w+):(?<id>\d+)>/ =~ mention
|
459
|
-
emoji(id) || Emoji.new({ 'animated' => !animated.nil?, 'name' => name, 'id' => id }, self, nil)
|
460
|
-
end
|
498
|
+
parse_mentions(mention, server).first
|
461
499
|
end
|
462
500
|
|
463
501
|
# Updates presence status.
|
@@ -480,7 +518,7 @@ module Discordrb
|
|
480
518
|
@gateway.send_status_update(status, since, activity_obj, afk)
|
481
519
|
|
482
520
|
# Update the status in the cache
|
483
|
-
profile.update_presence('status' => status.to_s, '
|
521
|
+
profile.update_presence('status' => status.to_s, 'activities' => [activity_obj].compact)
|
484
522
|
end
|
485
523
|
|
486
524
|
# Sets the currently playing game to the specified game.
|
@@ -489,7 +527,6 @@ module Discordrb
|
|
489
527
|
def game=(name)
|
490
528
|
gateway_check
|
491
529
|
update_status(@status, name, nil)
|
492
|
-
name
|
493
530
|
end
|
494
531
|
|
495
532
|
alias_method :playing=, :game=
|
@@ -500,7 +537,6 @@ module Discordrb
|
|
500
537
|
def listening=(name)
|
501
538
|
gateway_check
|
502
539
|
update_status(@status, name, nil, nil, nil, 2)
|
503
|
-
name
|
504
540
|
end
|
505
541
|
|
506
542
|
# Sets the current watching status to the specified name.
|
@@ -509,7 +545,6 @@ module Discordrb
|
|
509
545
|
def watching=(name)
|
510
546
|
gateway_check
|
511
547
|
update_status(@status, name, nil, nil, nil, 3)
|
512
|
-
name
|
513
548
|
end
|
514
549
|
|
515
550
|
# Sets the currently online stream to the specified name and Twitch URL.
|
@@ -576,6 +611,7 @@ module Discordrb
|
|
576
611
|
# @deprecated Will be changed to blocking behavior in v4.0. Use {#add_await!} instead.
|
577
612
|
def add_await(key, type, attributes = {}, &block)
|
578
613
|
raise "You can't await an AwaitEvent!" if type == Discordrb::Events::AwaitEvent
|
614
|
+
|
579
615
|
await = Await.new(self, key, type, attributes, block)
|
580
616
|
@awaits ||= {}
|
581
617
|
@awaits[key] = await
|
@@ -583,14 +619,17 @@ module Discordrb
|
|
583
619
|
|
584
620
|
# Awaits an event, blocking the current thread until a response is received.
|
585
621
|
# @param type [Class] The event class that should be listened for.
|
586
|
-
# @option attributes [Numeric] :timeout the amount of time to wait for a response before returning `nil`. Waits forever if omitted.
|
622
|
+
# @option attributes [Numeric] :timeout the amount of time (in seconds) to wait for a response before returning `nil`. Waits forever if omitted.
|
623
|
+
# @yield Executed when a matching event is received.
|
624
|
+
# @yieldparam event [Event] The event object that was triggered.
|
625
|
+
# @yieldreturn [true, false] Whether the event matches extra await criteria described by the block
|
587
626
|
# @return [Event, nil] The event object that was triggered, or `nil` if a `timeout` was set and no event was raised in time.
|
588
627
|
# @raise [ArgumentError] if `timeout` is given and is not a positive numeric value
|
589
628
|
def add_await!(type, attributes = {})
|
590
629
|
raise "You can't await an AwaitEvent!" if type == Discordrb::Events::AwaitEvent
|
591
630
|
|
592
631
|
timeout = attributes[:timeout]
|
593
|
-
raise ArgumentError, 'Timeout must be a number > 0' if timeout
|
632
|
+
raise ArgumentError, 'Timeout must be a number > 0' if timeout.is_a?(Numeric) && !timeout.positive?
|
594
633
|
|
595
634
|
mutex = Mutex.new
|
596
635
|
cv = ConditionVariable.new
|
@@ -598,7 +637,12 @@ module Discordrb
|
|
598
637
|
block = lambda do |event|
|
599
638
|
mutex.synchronize do
|
600
639
|
response = event
|
601
|
-
|
640
|
+
if block_given?
|
641
|
+
result = yield(event)
|
642
|
+
cv.signal if result.is_a?(TrueClass)
|
643
|
+
else
|
644
|
+
cv.signal
|
645
|
+
end
|
602
646
|
end
|
603
647
|
end
|
604
648
|
|
@@ -615,25 +659,26 @@ module Discordrb
|
|
615
659
|
|
616
660
|
remove_handler(handler)
|
617
661
|
raise 'ConditionVariable was signaled without returning an event!' if response.nil? && timeout.nil?
|
662
|
+
|
618
663
|
response
|
619
664
|
end
|
620
665
|
|
621
666
|
# Add a user to the list of ignored users. Those users will be ignored in message events at event processing level.
|
622
667
|
# @note Ignoring a user only prevents any message events (including mentions, commands etc.) from them! Typing and
|
623
668
|
# presence and any other events will still be received.
|
624
|
-
# @param user [User,
|
669
|
+
# @param user [User, String, Integer] The user, or its ID, to be ignored.
|
625
670
|
def ignore_user(user)
|
626
671
|
@ignored_ids << user.resolve_id
|
627
672
|
end
|
628
673
|
|
629
674
|
# Remove a user from the ignore list.
|
630
|
-
# @param user [User,
|
675
|
+
# @param user [User, String, Integer] The user, or its ID, to be unignored.
|
631
676
|
def unignore_user(user)
|
632
677
|
@ignored_ids.delete(user.resolve_id)
|
633
678
|
end
|
634
679
|
|
635
680
|
# Checks whether a user is being ignored.
|
636
|
-
# @param user [User,
|
681
|
+
# @param user [User, String, Integer] The user, or its ID, to check.
|
637
682
|
# @return [true, false] whether or not the user is ignored.
|
638
683
|
def ignored?(user)
|
639
684
|
@ignored_ids.include?(user.resolve_id)
|
@@ -677,7 +722,8 @@ module Discordrb
|
|
677
722
|
# e.g. due to a Discord outage or because the servers are large and taking a while to load.
|
678
723
|
def unavailable_servers_check
|
679
724
|
# Return unless there are servers that are unavailable.
|
680
|
-
return unless @unavailable_servers
|
725
|
+
return unless @unavailable_servers&.positive?
|
726
|
+
|
681
727
|
LOGGER.warn("#{@unavailable_servers} servers haven't been cached yet.")
|
682
728
|
LOGGER.warn('Servers may be unavailable due to an outage, or your bot is on very large servers that are taking a while to load.')
|
683
729
|
end
|
@@ -737,10 +783,21 @@ module Discordrb
|
|
737
783
|
|
738
784
|
user_id = data['user_id'].to_i
|
739
785
|
old_voice_state = server.voice_states[user_id]
|
740
|
-
old_channel_id = old_voice_state.voice_channel
|
786
|
+
old_channel_id = old_voice_state.voice_channel&.id if old_voice_state
|
741
787
|
|
742
788
|
server.update_voice_state(data)
|
743
789
|
|
790
|
+
existing_voice = @voices[server_id]
|
791
|
+
if user_id == @profile.id && existing_voice
|
792
|
+
new_channel_id = data['channel_id']
|
793
|
+
if new_channel_id
|
794
|
+
new_channel = channel(new_channel_id)
|
795
|
+
existing_voice.channel = new_channel
|
796
|
+
else
|
797
|
+
voice_destroy(server_id)
|
798
|
+
end
|
799
|
+
end
|
800
|
+
|
744
801
|
old_channel_id
|
745
802
|
end
|
746
803
|
|
@@ -751,6 +808,7 @@ module Discordrb
|
|
751
808
|
|
752
809
|
debug("Voice server update received! chan: #{channel.inspect}")
|
753
810
|
return unless channel
|
811
|
+
|
754
812
|
@should_connect_to_voice.delete(server_id)
|
755
813
|
debug('Updating voice server!')
|
756
814
|
|
@@ -763,7 +821,7 @@ module Discordrb
|
|
763
821
|
end
|
764
822
|
|
765
823
|
debug('Got data, now creating the bot.')
|
766
|
-
@voices[server_id] = Discordrb::Voice::VoiceBot.new(channel, self, token, @session_id, endpoint
|
824
|
+
@voices[server_id] = Discordrb::Voice::VoiceBot.new(channel, self, token, @session_id, endpoint)
|
767
825
|
end
|
768
826
|
|
769
827
|
# Internal handler for CHANNEL_CREATE
|
@@ -787,6 +845,7 @@ module Discordrb
|
|
787
845
|
channel = Channel.new(data, self)
|
788
846
|
old_channel = @channels[channel.id]
|
789
847
|
return unless old_channel
|
848
|
+
|
790
849
|
old_channel.update_from(channel)
|
791
850
|
end
|
792
851
|
|
@@ -843,12 +902,14 @@ module Discordrb
|
|
843
902
|
member = server.member(data['user']['id'].to_i)
|
844
903
|
member.update_roles(data['roles'])
|
845
904
|
member.update_nick(data['nick'])
|
905
|
+
member.update_boosting_since(data['premium_since'])
|
846
906
|
end
|
847
907
|
|
848
908
|
# Internal handler for GUILD_MEMBER_DELETE
|
849
909
|
def delete_guild_member(data)
|
850
910
|
server_id = data['guild_id'].to_i
|
851
911
|
server = self.server(server_id)
|
912
|
+
return unless server
|
852
913
|
|
853
914
|
user_id = data['user']['id'].to_i
|
854
915
|
server.delete_member(user_id)
|
@@ -951,13 +1012,13 @@ module Discordrb
|
|
951
1012
|
# Remove the "Bot " prefix if it exists
|
952
1013
|
token = token[4..-1] if token.start_with? 'Bot '
|
953
1014
|
|
954
|
-
token =
|
1015
|
+
token = "Bot #{token}" unless type == :user
|
955
1016
|
token
|
956
1017
|
end
|
957
1018
|
|
958
1019
|
def handle_dispatch(type, data)
|
959
1020
|
# Check whether there are still unavailable servers and there have been more than 10 seconds since READY
|
960
|
-
if @unavailable_servers &&
|
1021
|
+
if @unavailable_servers&.positive? && (Time.now - @unavailable_timeout_time) > 10 && !((@intents || 0) & INTENTS[:servers]).zero?
|
961
1022
|
# The server streaming timed out!
|
962
1023
|
LOGGER.debug("Server streaming timed out with #{@unavailable_servers} servers remaining")
|
963
1024
|
LOGGER.debug('Calling ready now because server loading is taking a long time. Servers may be unavailable due to an outage, or your bot is on very large servers.')
|
@@ -1019,6 +1080,11 @@ module Discordrb
|
|
1019
1080
|
id = data['guild_id'].to_i
|
1020
1081
|
server = server(id)
|
1021
1082
|
server.process_chunk(data['members'])
|
1083
|
+
when :INVITE_CREATE
|
1084
|
+
invite = Invite.new(data, self)
|
1085
|
+
raise_event(InviteCreateEvent.new(data, invite, self))
|
1086
|
+
when :INVITE_DELETE
|
1087
|
+
raise_event(InviteDeleteEvent.new(data, self))
|
1022
1088
|
when :MESSAGE_CREATE
|
1023
1089
|
if ignored?(data['author']['id'].to_i)
|
1024
1090
|
debug("Ignored author with ID #{data['author']['id']}")
|
@@ -1053,6 +1119,10 @@ module Discordrb
|
|
1053
1119
|
update_message(data)
|
1054
1120
|
|
1055
1121
|
message = Message.new(data, self)
|
1122
|
+
|
1123
|
+
event = MessageUpdateEvent.new(message, self)
|
1124
|
+
raise_event(event)
|
1125
|
+
|
1056
1126
|
return if message.from_bot? && !should_parse_self
|
1057
1127
|
|
1058
1128
|
unless message.author
|
@@ -1120,10 +1190,10 @@ module Discordrb
|
|
1120
1190
|
played_before = presence_user.nil? ? nil : presence_user.game
|
1121
1191
|
update_presence(data)
|
1122
1192
|
|
1123
|
-
event = if now_playing
|
1124
|
-
PlayingEvent.new(data, self)
|
1125
|
-
else
|
1193
|
+
event = if now_playing == played_before
|
1126
1194
|
PresenceEvent.new(data, self)
|
1195
|
+
else
|
1196
|
+
PlayingEvent.new(data, self)
|
1127
1197
|
end
|
1128
1198
|
|
1129
1199
|
raise_event(event)
|
@@ -1135,7 +1205,8 @@ module Discordrb
|
|
1135
1205
|
when :VOICE_SERVER_UPDATE
|
1136
1206
|
update_voice_server(data)
|
1137
1207
|
|
1138
|
-
|
1208
|
+
event = VoiceServerUpdateEvent.new(data, self)
|
1209
|
+
raise_event(event)
|
1139
1210
|
when :CHANNEL_CREATE
|
1140
1211
|
create_channel(data)
|
1141
1212
|
|
@@ -1300,6 +1371,7 @@ module Discordrb
|
|
1300
1371
|
@event_handlers ||= {}
|
1301
1372
|
handlers = @event_handlers[event.class]
|
1302
1373
|
return unless handlers
|
1374
|
+
|
1303
1375
|
handlers.dup.each do |handler|
|
1304
1376
|
call_event(handler, event) if handler.matches?(event)
|
1305
1377
|
end
|
@@ -1315,7 +1387,7 @@ module Discordrb
|
|
1315
1387
|
begin
|
1316
1388
|
handler.call(event)
|
1317
1389
|
handler.after_call(event)
|
1318
|
-
rescue => e
|
1390
|
+
rescue StandardError => e
|
1319
1391
|
log_exception(e)
|
1320
1392
|
ensure
|
1321
1393
|
@event_threads.delete(t)
|
@@ -1328,6 +1400,7 @@ module Discordrb
|
|
1328
1400
|
@awaits.each do |_, await|
|
1329
1401
|
key, should_delete = await.match(event)
|
1330
1402
|
next unless key
|
1403
|
+
|
1331
1404
|
debug("should_delete: #{should_delete}")
|
1332
1405
|
@awaits.delete(await.key) if should_delete
|
1333
1406
|
|
@@ -1335,5 +1408,24 @@ module Discordrb
|
|
1335
1408
|
raise_event(await_event)
|
1336
1409
|
end
|
1337
1410
|
end
|
1411
|
+
|
1412
|
+
def calculate_intents(intents)
|
1413
|
+
intents.reduce(0) do |sum, intent|
|
1414
|
+
case intent
|
1415
|
+
when Symbol
|
1416
|
+
if INTENTS[intent]
|
1417
|
+
sum | INTENTS[intent]
|
1418
|
+
else
|
1419
|
+
LOGGER.warn("Unknown intent: #{intent}")
|
1420
|
+
sum
|
1421
|
+
end
|
1422
|
+
when Integer
|
1423
|
+
sum | intent
|
1424
|
+
else
|
1425
|
+
LOGGER.warn("Invalid intent: #{intent}")
|
1426
|
+
sum
|
1427
|
+
end
|
1428
|
+
end
|
1429
|
+
end
|
1338
1430
|
end
|
1339
1431
|
end
|