discordrb 3.2.1 → 3.3.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 +5 -5
- data/.gitignore +4 -0
- data/.rubocop.yml +3 -3
- data/.travis.yml +28 -3
- data/.yardopts +1 -1
- data/CHANGELOG.md +555 -144
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +0 -4
- data/README.md +86 -15
- data/Rakefile +2 -2
- data/bin/travis_build_docs.sh +17 -0
- data/discordrb-webhooks.gemspec +2 -1
- data/discordrb.gemspec +12 -5
- data/lib/discordrb.rb +2 -2
- data/lib/discordrb/api.rb +94 -25
- data/lib/discordrb/api/channel.rb +53 -17
- data/lib/discordrb/api/invite.rb +7 -4
- data/lib/discordrb/api/server.rb +173 -36
- data/lib/discordrb/api/user.rb +18 -4
- data/lib/discordrb/api/webhook.rb +83 -0
- data/lib/discordrb/await.rb +1 -1
- data/lib/discordrb/bot.rb +191 -102
- data/lib/discordrb/cache.rb +39 -9
- data/lib/discordrb/commands/command_bot.rb +79 -24
- data/lib/discordrb/commands/container.rb +16 -2
- data/lib/discordrb/commands/parser.rb +46 -7
- data/lib/discordrb/commands/rate_limiter.rb +8 -6
- data/lib/discordrb/container.rb +51 -7
- data/lib/discordrb/data.rb +1729 -286
- data/lib/discordrb/errors.rb +34 -1
- data/lib/discordrb/events/generic.rb +1 -1
- data/lib/discordrb/events/guilds.rb +1 -0
- data/lib/discordrb/events/message.rb +18 -12
- data/lib/discordrb/events/presence.rb +7 -2
- data/lib/discordrb/events/reactions.rb +13 -4
- data/lib/discordrb/events/roles.rb +7 -6
- data/lib/discordrb/events/typing.rb +1 -1
- data/lib/discordrb/events/webhooks.rb +61 -0
- data/lib/discordrb/gateway.rb +85 -32
- data/lib/discordrb/light.rb +1 -1
- data/lib/discordrb/logger.rb +8 -7
- data/lib/discordrb/permissions.rb +41 -4
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +10 -8
- data/lib/discordrb/voice/voice_bot.rb +4 -4
- data/lib/discordrb/websocket.rb +2 -2
- metadata +59 -14
data/lib/discordrb/errors.rb
CHANGED
@@ -102,6 +102,9 @@ module Discordrb
|
|
102
102
|
# Unknown User
|
103
103
|
UnknownUser = Code(10_013)
|
104
104
|
|
105
|
+
# Unknown Emoji
|
106
|
+
UnknownEmoji = Code(10_014)
|
107
|
+
|
105
108
|
# Bots cannot use this endpoint
|
106
109
|
EndpointNotForBots = Code(20_001)
|
107
110
|
|
@@ -114,6 +117,18 @@ module Discordrb
|
|
114
117
|
# Maximum number of friends reached (1000)
|
115
118
|
FriendLimitReached = Code(30_002)
|
116
119
|
|
120
|
+
# Maximum number of pins reached (50)
|
121
|
+
PinLimitReached = Code(30_003)
|
122
|
+
|
123
|
+
# Maximum number of guild roles reached (250)
|
124
|
+
RoleLimitReached = Code(30_005)
|
125
|
+
|
126
|
+
# Too many reactions
|
127
|
+
ReactionLimitReached = Code(30_010)
|
128
|
+
|
129
|
+
# Maximum number of guild channels reached (500)
|
130
|
+
ChannelLimitReached = Code(30_013)
|
131
|
+
|
117
132
|
# Unauthorized
|
118
133
|
Unauthorized = Unauthorised = Code(40_001)
|
119
134
|
|
@@ -124,7 +139,7 @@ module Discordrb
|
|
124
139
|
InvalidAccountType = Code(50_002)
|
125
140
|
|
126
141
|
# Cannot execute action on a DM channel
|
127
|
-
|
142
|
+
InvalidActionForDM = Code(50_003)
|
128
143
|
|
129
144
|
# Embed Disabled
|
130
145
|
EmbedDisabled = Code(50_004)
|
@@ -164,5 +179,23 @@ module Discordrb
|
|
164
179
|
|
165
180
|
# Provided too few or too many messages to delete. Must provide at least 2 and fewer than 100 messages to delete.
|
166
181
|
InvalidBulkDeleteCount = Code(50_016)
|
182
|
+
|
183
|
+
# A message can only be pinned to the channel it was sent in
|
184
|
+
CannotPinInDifferentChannel = Code(50_019)
|
185
|
+
|
186
|
+
# Cannot execute action on a system message
|
187
|
+
InvalidActionForSystemMessage = Code(50_021)
|
188
|
+
|
189
|
+
# A message provided was too old to bulk delete
|
190
|
+
MessageTooOld = Code(50_034)
|
191
|
+
|
192
|
+
# Invalid Form Body
|
193
|
+
InvalidFormBody = Code(50_035)
|
194
|
+
|
195
|
+
# An invite was accepted to a guild the application's bot is not in
|
196
|
+
MissingBotMember = Code(50_036)
|
197
|
+
|
198
|
+
# Reaction Blocked
|
199
|
+
ReactionBlocked = Code(90_001)
|
167
200
|
end
|
168
201
|
end
|
@@ -19,7 +19,7 @@ module Discordrb::Events
|
|
19
19
|
# 2. A single attribute, negated
|
20
20
|
# 3. An array of attributes, not negated
|
21
21
|
# 4. An array of attributes, not negated
|
22
|
-
# Note that it doesn't allow an array of negated attributes. For info on negation stuff, see {not!}
|
22
|
+
# Note that it doesn't allow an array of negated attributes. For info on negation stuff, see {::#not!}
|
23
23
|
# @param attributes [Object, Array<Object>, Negated<Object>, Negated<Array<Object>>, nil] One or more attributes to
|
24
24
|
# compare to the to_check value.
|
25
25
|
# @param to_check [Object] What to compare the attributes to.
|
@@ -12,9 +12,22 @@ module Discordrb::Events
|
|
12
12
|
# Sends a message to the channel this message was sent in, right now. It is usually preferable to use {#<<} instead
|
13
13
|
# because it avoids rate limiting problems
|
14
14
|
# @param content [String] The message to send to the channel
|
15
|
+
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
|
16
|
+
# @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
|
15
17
|
# @return [Discordrb::Message] the message that was sent
|
16
|
-
def send_message(content)
|
17
|
-
channel.send_message(content)
|
18
|
+
def send_message(content, tts = false, embed = nil)
|
19
|
+
channel.send_message(content, tts, embed)
|
20
|
+
end
|
21
|
+
|
22
|
+
# The same as {#send_message}, but yields a {Webhooks::Embed} for easy building of embedded content inside a block.
|
23
|
+
# @see Channel#send_embed
|
24
|
+
# @param message [String] The message that should be sent along with the embed. If this is the empty string, only the embed will be shown.
|
25
|
+
# @param embed [Discordrb::Webhooks::Embed, nil] The embed to start the building process with, or nil if one should be created anew.
|
26
|
+
# @yield [embed] Yields the embed to allow for easy building inside a block.
|
27
|
+
# @yieldparam embed [Discordrb::Webhooks::Embed] The embed from the parameters, or a new one.
|
28
|
+
# @return [Message] The resulting message.
|
29
|
+
def send_embed(message = '', embed = nil, &block)
|
30
|
+
channel.send_embed(message, embed, &block)
|
18
31
|
end
|
19
32
|
|
20
33
|
# Sends a temporary message to the channel this message was sent in, right now.
|
@@ -99,20 +112,14 @@ module Discordrb::Events
|
|
99
112
|
@file = nil
|
100
113
|
end
|
101
114
|
|
102
|
-
# Sends a message to the channel this message was sent in, right now. It is usually preferable to use {#<<} instead
|
103
|
-
# because it avoids rate limiting problems
|
104
|
-
# @param content [String] The message to send to the channel
|
105
|
-
# @return [Discordrb::Message] the message that was sent
|
106
|
-
def send_message(content)
|
107
|
-
@message.channel.send_message(content)
|
108
|
-
end
|
109
|
-
|
110
115
|
# Sends file with a caption to the channel this message was sent in, right now.
|
111
116
|
# It is usually preferable to use {#<<} and {#attach_file} instead
|
112
117
|
# because it avoids rate limiting problems
|
113
118
|
# @param file [File] The file to send to the channel
|
114
119
|
# @param caption [String] The caption attached to the file
|
115
120
|
# @return [Discordrb::Message] the message that was sent
|
121
|
+
# @example Send a file from disk
|
122
|
+
# event.send_file(File.open('rubytaco.png', 'r'))
|
116
123
|
def send_file(file, caption: nil)
|
117
124
|
@message.channel.send_file(file, caption: caption)
|
118
125
|
end
|
@@ -129,7 +136,6 @@ module Discordrb::Events
|
|
129
136
|
# Detaches a file from the message event.
|
130
137
|
def detach_file
|
131
138
|
@file = nil
|
132
|
-
nil
|
133
139
|
end
|
134
140
|
|
135
141
|
# @return [true, false] whether or not this message was sent by the bot itself
|
@@ -165,7 +171,7 @@ module Discordrb::Events
|
|
165
171
|
if a.is_a? String
|
166
172
|
e.end_with? a
|
167
173
|
elsif a.is_a? Regexp
|
168
|
-
|
174
|
+
!(e =~ Regexp.new("#{a}$")).nil?
|
169
175
|
end
|
170
176
|
end,
|
171
177
|
matches_all(@attributes[:containing] || @attributes[:contains], event.content) do |a, e|
|
@@ -65,17 +65,22 @@ module Discordrb::Events
|
|
65
65
|
# @return [String] the URL to the stream
|
66
66
|
attr_reader :url
|
67
67
|
|
68
|
+
# @return [String] what the player is currently doing (ex. game being streamed)
|
69
|
+
attr_reader :details
|
70
|
+
|
68
71
|
# @return [Integer] the type of play. 0 = game, 1 = Twitch
|
69
72
|
attr_reader :type
|
70
73
|
|
71
74
|
def initialize(data, bot)
|
72
75
|
@bot = bot
|
73
76
|
|
77
|
+
@server = bot.server(data['guild_id'].to_i)
|
74
78
|
@user = bot.user(data['user']['id'].to_i)
|
75
79
|
@game = data['game'] ? data['game']['name'] : nil
|
76
|
-
@server = bot.server(data['guild_id'].to_i)
|
77
|
-
@url = data['game'] ? data['game']['url'] : nil
|
78
80
|
@type = data['game'] ? data['game']['type'].to_i : nil
|
81
|
+
# Handle optional 'game' fields safely
|
82
|
+
@url = data['game'] && data['game']['url'] ? data['game']['url'] : nil
|
83
|
+
@details = data['game'] && data['game']['details'] ? data['game']['details'] : nil
|
79
84
|
end
|
80
85
|
end
|
81
86
|
|
@@ -5,7 +5,7 @@ require 'discordrb/data'
|
|
5
5
|
|
6
6
|
module Discordrb::Events
|
7
7
|
# Generic superclass for events about adding and removing reactions
|
8
|
-
class ReactionEvent
|
8
|
+
class ReactionEvent < Event
|
9
9
|
include Respondable
|
10
10
|
|
11
11
|
# @return [Emoji] the emoji that was reacted with.
|
@@ -20,10 +20,14 @@ module Discordrb::Events
|
|
20
20
|
@channel_id = data['channel_id'].to_i
|
21
21
|
end
|
22
22
|
|
23
|
-
# @return [User] the user that reacted to this message.
|
23
|
+
# @return [User, Member] the user that reacted to this message, or member if a server exists.
|
24
24
|
def user
|
25
25
|
# Cache the user so we don't do requests all the time
|
26
|
-
@user ||=
|
26
|
+
@user ||= if server
|
27
|
+
@server.member(@user_id)
|
28
|
+
else
|
29
|
+
@bot.user(@user_id)
|
30
|
+
end
|
27
31
|
end
|
28
32
|
|
29
33
|
# @return [Message] the message that was reacted to.
|
@@ -35,6 +39,11 @@ module Discordrb::Events
|
|
35
39
|
def channel
|
36
40
|
@channel ||= @bot.channel(@channel_id)
|
37
41
|
end
|
42
|
+
|
43
|
+
# @return [Server, nil] the server that was reacted in. If reacted in a PM channel, it will be nil.
|
44
|
+
def server
|
45
|
+
@server ||= channel.server
|
46
|
+
end
|
38
47
|
end
|
39
48
|
|
40
49
|
# Generic superclass for event handlers pertaining to adding and removing reactions
|
@@ -70,7 +79,7 @@ module Discordrb::Events
|
|
70
79
|
class ReactionRemoveEventHandler < ReactionEventHandler; end
|
71
80
|
|
72
81
|
# Event raised when somebody removes all reactions from a message
|
73
|
-
class ReactionRemoveAllEvent
|
82
|
+
class ReactionRemoveAllEvent < Event
|
74
83
|
include Respondable
|
75
84
|
|
76
85
|
def initialize(data, bot)
|
@@ -12,6 +12,11 @@ module Discordrb::Events
|
|
12
12
|
# @return [Server] the server on which a role got created
|
13
13
|
attr_reader :server
|
14
14
|
|
15
|
+
# @!attribute [r] name
|
16
|
+
# @return [String] this role's name
|
17
|
+
# @see Role#name
|
18
|
+
delegate :name, to: :role
|
19
|
+
|
15
20
|
def initialize(data, bot)
|
16
21
|
@bot = bot
|
17
22
|
|
@@ -68,12 +73,8 @@ module Discordrb::Events
|
|
68
73
|
return false unless event.is_a? ServerRoleDeleteEvent
|
69
74
|
|
70
75
|
[
|
71
|
-
matches_all(@attributes[:
|
72
|
-
a ==
|
73
|
-
e.to_s
|
74
|
-
else
|
75
|
-
e
|
76
|
-
end
|
76
|
+
matches_all(@attributes[:id], event.id) do |a, e|
|
77
|
+
a.resolve_id == e.resolve_id
|
77
78
|
end
|
78
79
|
].reduce(true, &:&)
|
79
80
|
end
|
@@ -10,7 +10,7 @@ module Discordrb::Events
|
|
10
10
|
# @return [Channel] the channel on which a user started typing.
|
11
11
|
attr_reader :channel
|
12
12
|
|
13
|
-
# @return [Member] the user that started typing.
|
13
|
+
# @return [User, Member, Recipient] the user that started typing.
|
14
14
|
attr_reader :user
|
15
15
|
alias_method :member, :user
|
16
16
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'discordrb/events/generic'
|
4
|
+
require 'discordrb/data'
|
5
|
+
|
6
|
+
module Discordrb::Events
|
7
|
+
# Event raised when a webhook is updated
|
8
|
+
class WebhookUpdateEvent < Event
|
9
|
+
# @return [Server] the server where the webhook updated
|
10
|
+
attr_reader :server
|
11
|
+
|
12
|
+
# @return [Channel] the channel the webhook is associated to
|
13
|
+
attr_reader :channel
|
14
|
+
|
15
|
+
def initialize(data, bot)
|
16
|
+
@bot = bot
|
17
|
+
|
18
|
+
@server = bot.server(data['guild_id'].to_i)
|
19
|
+
@channel = bot.channel(data['channel_id'].to_i)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Event handler for {WebhookUpdateEvent}
|
24
|
+
class WebhookUpdateEventHandler < EventHandler
|
25
|
+
def matches?(event)
|
26
|
+
# Check for the proper event type
|
27
|
+
return false unless event.is_a? WebhookUpdateEvent
|
28
|
+
|
29
|
+
[
|
30
|
+
matches_all(@attributes[:server], event.server) do |a, e|
|
31
|
+
a == if a.is_a? String
|
32
|
+
e.name
|
33
|
+
elsif a.is_a? Integer
|
34
|
+
e.id
|
35
|
+
else
|
36
|
+
e
|
37
|
+
end
|
38
|
+
end,
|
39
|
+
matches_all(@attributes[:channel], event.channel) do |a, e|
|
40
|
+
if a.is_a? String
|
41
|
+
# Make sure to remove the "#" from channel names in case it was specified
|
42
|
+
a.delete('#') == e.name
|
43
|
+
elsif a.is_a? Integer
|
44
|
+
a == e.id
|
45
|
+
else
|
46
|
+
a == e
|
47
|
+
end
|
48
|
+
end,
|
49
|
+
matches_all(@attributes[:webhook], event) do |a, e|
|
50
|
+
a == if a.is_a? String
|
51
|
+
e.name
|
52
|
+
elsif a.is_a? Integer
|
53
|
+
e.id
|
54
|
+
else
|
55
|
+
e
|
56
|
+
end
|
57
|
+
end
|
58
|
+
].reduce(true, &:&)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/discordrb/gateway.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This file uses code from Websocket::Client::Simple, licensed under the following license:
|
2
4
|
#
|
3
5
|
# Copyright (c) 2013-2014 Sho Hashimoto
|
@@ -100,6 +102,7 @@ module Discordrb
|
|
100
102
|
@invalid = false
|
101
103
|
end
|
102
104
|
|
105
|
+
# Flags this session as suspended, so we know not to try and send heartbeats, etc. to the gateway until we've reconnected
|
103
106
|
def suspend
|
104
107
|
@suspended = true
|
105
108
|
end
|
@@ -108,10 +111,12 @@ module Discordrb
|
|
108
111
|
@suspended
|
109
112
|
end
|
110
113
|
|
114
|
+
# Flags this session as no longer being suspended, so we can resume
|
111
115
|
def resume
|
112
116
|
@suspended = false
|
113
117
|
end
|
114
118
|
|
119
|
+
# Flags this session as being invalid
|
115
120
|
def invalidate
|
116
121
|
@invalid = true
|
117
122
|
end
|
@@ -140,7 +145,7 @@ module Discordrb
|
|
140
145
|
# @return [true, false] whether or not this gateway should check for heartbeat ACKs.
|
141
146
|
attr_accessor :check_heartbeat_acks
|
142
147
|
|
143
|
-
def initialize(bot, token, shard_key = nil)
|
148
|
+
def initialize(bot, token, shard_key = nil, compress_mode = :stream)
|
144
149
|
@token = token
|
145
150
|
@bot = bot
|
146
151
|
|
@@ -152,6 +157,8 @@ module Discordrb
|
|
152
157
|
@ws_success = false
|
153
158
|
|
154
159
|
@check_heartbeat_acks = true
|
160
|
+
|
161
|
+
@compress_mode = compress_mode
|
155
162
|
end
|
156
163
|
|
157
164
|
# Connect to the gateway server in a separate thread
|
@@ -167,7 +174,7 @@ module Discordrb
|
|
167
174
|
LOGGER.debug('Confirmation received! Exiting run.')
|
168
175
|
end
|
169
176
|
|
170
|
-
# Prevents all further execution until the websocket thread stops (e.
|
177
|
+
# Prevents all further execution until the websocket thread stops (e.g. through a closed connection).
|
171
178
|
def sync
|
172
179
|
@ws_thread.join
|
173
180
|
end
|
@@ -260,13 +267,14 @@ module Discordrb
|
|
260
267
|
# Identifies to Discord with the default parameters.
|
261
268
|
# @see #send_identify
|
262
269
|
def identify
|
270
|
+
compress = @compress_mode == :large
|
263
271
|
send_identify(@token, {
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
},
|
272
|
+
'$os': RUBY_PLATFORM,
|
273
|
+
'$browser': 'discordrb',
|
274
|
+
'$device': 'discordrb',
|
275
|
+
'$referrer': '',
|
276
|
+
'$referring_domain': ''
|
277
|
+
}, compress, 100, @shard_key)
|
270
278
|
end
|
271
279
|
|
272
280
|
# Sends an identify packet (op 2). This starts a new session on the current connection and tells Discord who we are.
|
@@ -347,7 +355,7 @@ module Discordrb
|
|
347
355
|
# Reconnects the gateway connection in a controlled manner.
|
348
356
|
# @param attempt_resume [true, false] Whether a resume should be attempted after the reconnection.
|
349
357
|
def reconnect(attempt_resume = true)
|
350
|
-
@session.suspend if attempt_resume
|
358
|
+
@session.suspend if @session && attempt_resume
|
351
359
|
|
352
360
|
@instant_reconnect = true
|
353
361
|
@should_reconnect = true
|
@@ -392,6 +400,29 @@ module Discordrb
|
|
392
400
|
send_packet(Opcodes::REQUEST_MEMBERS, data)
|
393
401
|
end
|
394
402
|
|
403
|
+
# Sends a custom packet over the connection. This can be useful to implement future yet unimplemented functionality
|
404
|
+
# or for testing. You probably shouldn't use this unless you know what you're doing.
|
405
|
+
# @param op [Integer] The opcode the packet should be sent as. Can be one of {Opcodes} or a custom value if
|
406
|
+
# necessary.
|
407
|
+
# @param packet [Object] Some arbitrary JSON-serialisable data that should be sent as the `d` field.
|
408
|
+
def send_packet(op, packet)
|
409
|
+
data = {
|
410
|
+
op: op,
|
411
|
+
d: packet
|
412
|
+
}
|
413
|
+
|
414
|
+
send(data.to_json)
|
415
|
+
end
|
416
|
+
|
417
|
+
# Sends custom raw data over the connection. Only useful for testing; even if you know what you're doing you
|
418
|
+
# probably want to use {#send_packet} instead.
|
419
|
+
# @param data [String] The data to send.
|
420
|
+
# @param type [Symbol] The type the WebSocket frame should have; either `:text`, `:binary`, `:ping`, `:pong`, or
|
421
|
+
# `:close`.
|
422
|
+
def send_raw(data, type = :text)
|
423
|
+
send(data, type)
|
424
|
+
end
|
425
|
+
|
395
426
|
private
|
396
427
|
|
397
428
|
def setup_heartbeats(interval)
|
@@ -440,7 +471,7 @@ module Discordrb
|
|
440
471
|
wait_for_reconnect
|
441
472
|
end
|
442
473
|
|
443
|
-
# Restart the loop, i.
|
474
|
+
# Restart the loop, i.e. reconnect
|
444
475
|
end
|
445
476
|
end
|
446
477
|
|
@@ -462,14 +493,19 @@ module Discordrb
|
|
462
493
|
|
463
494
|
if secure_uri?(uri)
|
464
495
|
ctx = OpenSSL::SSL::SSLContext.new
|
465
|
-
ctx.ssl_version = 'SSLv23'
|
466
|
-
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # use VERIFY_PEER for verification
|
467
496
|
|
468
|
-
|
469
|
-
|
470
|
-
|
497
|
+
if ENV['DISCORDRB_SSL_VERIFY_NONE']
|
498
|
+
ctx.ssl_version = 'SSLv23'
|
499
|
+
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE # use VERIFY_PEER for verification
|
471
500
|
|
472
|
-
|
501
|
+
cert_store = OpenSSL::X509::Store.new
|
502
|
+
cert_store.set_default_paths
|
503
|
+
ctx.cert_store = cert_store
|
504
|
+
else
|
505
|
+
ctx.set_params ssl_version: :TLSv1_2
|
506
|
+
end
|
507
|
+
|
508
|
+
socket = OpenSSL::SSL::SSLSocket.new(socket, ctx)
|
473
509
|
socket.connect
|
474
510
|
end
|
475
511
|
|
@@ -478,7 +514,7 @@ module Discordrb
|
|
478
514
|
|
479
515
|
# Whether the URI is secure (connection should be encrypted)
|
480
516
|
def secure_uri?(uri)
|
481
|
-
%w
|
517
|
+
%w[https wss].include? uri.scheme
|
482
518
|
end
|
483
519
|
|
484
520
|
# The port we should connect to, if the URI doesn't have one set.
|
@@ -497,8 +533,13 @@ module Discordrb
|
|
497
533
|
# Append a slash in case it's not there (I'm not sure how well WSCS handles it otherwise)
|
498
534
|
raw_url += '/' unless raw_url.end_with? '/'
|
499
535
|
|
500
|
-
|
501
|
-
|
536
|
+
query = if @compress_mode == :stream
|
537
|
+
"?encoding=json&v=#{GATEWAY_VERSION}&compress=zlib-stream"
|
538
|
+
else
|
539
|
+
"?encoding=json&v=#{GATEWAY_VERSION}"
|
540
|
+
end
|
541
|
+
|
542
|
+
raw_url + query
|
502
543
|
end
|
503
544
|
|
504
545
|
def connect
|
@@ -511,6 +552,9 @@ module Discordrb
|
|
511
552
|
# Parse it
|
512
553
|
gateway_uri = URI.parse(url)
|
513
554
|
|
555
|
+
# Zlib context for this gateway connection
|
556
|
+
@zlib_reader = Zlib::Inflate.new
|
557
|
+
|
514
558
|
# Connect to the obtained URI with a socket
|
515
559
|
@socket = obtain_socket(gateway_uri)
|
516
560
|
LOGGER.debug('Obtained socket')
|
@@ -537,6 +581,11 @@ module Discordrb
|
|
537
581
|
|
538
582
|
until @closed
|
539
583
|
begin
|
584
|
+
unless @socket
|
585
|
+
LOGGER.warn('Socket is nil in websocket_loop! Reconnecting')
|
586
|
+
handle_internal_close('Socket is nil in websocket_loop')
|
587
|
+
end
|
588
|
+
|
540
589
|
recv_data = nil
|
541
590
|
|
542
591
|
# Get some data from the socket, synchronised so the socket can't be closed during this
|
@@ -593,10 +642,23 @@ module Discordrb
|
|
593
642
|
LOGGER.log_exception(e)
|
594
643
|
end
|
595
644
|
|
645
|
+
ZLIB_SUFFIX = "\x00\x00\xFF\xFF".b.freeze
|
646
|
+
|
596
647
|
def handle_message(msg)
|
597
|
-
if
|
598
|
-
|
599
|
-
|
648
|
+
if @compress_mode == :large
|
649
|
+
if msg.byteslice(0) == 'x'
|
650
|
+
# The message is compressed, inflate it
|
651
|
+
msg = Zlib::Inflate.inflate(msg)
|
652
|
+
end
|
653
|
+
elsif @compress_mode == :stream
|
654
|
+
# Write deflated string to buffer
|
655
|
+
@zlib_reader << msg
|
656
|
+
|
657
|
+
# Check if message ends in `ZLIB_SUFFIX`
|
658
|
+
return if msg.bytesize < 4 || msg.byteslice(-4, 4) != ZLIB_SUFFIX
|
659
|
+
|
660
|
+
# Inflate the deflated buffer
|
661
|
+
msg = @zlib_reader.inflate('')
|
600
662
|
end
|
601
663
|
|
602
664
|
# Parse packet
|
@@ -645,7 +707,7 @@ module Discordrb
|
|
645
707
|
# The RESUMED event is received after a successful op 6 (resume). It does nothing except tell the bot the
|
646
708
|
# connection is initiated (like READY would). Starting with v5, it doesn't set a new heartbeat interval anymore
|
647
709
|
# since that is handled by op 10 (HELLO).
|
648
|
-
LOGGER.
|
710
|
+
LOGGER.info 'Resumed'
|
649
711
|
return
|
650
712
|
end
|
651
713
|
|
@@ -726,15 +788,6 @@ module Discordrb
|
|
726
788
|
end
|
727
789
|
end
|
728
790
|
|
729
|
-
def send_packet(op, packet)
|
730
|
-
data = {
|
731
|
-
op: op,
|
732
|
-
d: packet
|
733
|
-
}
|
734
|
-
|
735
|
-
send(data.to_json)
|
736
|
-
end
|
737
|
-
|
738
791
|
def send(data, type = :text)
|
739
792
|
LOGGER.out(data)
|
740
793
|
|