discordrb 3.1.1 → 3.2.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/.codeclimate.yml +16 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +9 -0
- data/.travis.yml +2 -1
- data/CHANGELOG.md +49 -0
- data/CONTRIBUTING.md +13 -0
- data/Gemfile +6 -1
- data/README.md +2 -0
- data/Rakefile +12 -1
- data/discordrb-webhooks.gemspec +25 -0
- data/discordrb.gemspec +6 -4
- data/lib/discordrb/api.rb +5 -0
- data/lib/discordrb/api/channel.rb +74 -6
- data/lib/discordrb/api/server.rb +37 -0
- data/lib/discordrb/bot.rb +116 -29
- data/lib/discordrb/commands/command_bot.rb +93 -9
- data/lib/discordrb/commands/container.rb +3 -0
- data/lib/discordrb/commands/parser.rb +45 -29
- data/lib/discordrb/commands/rate_limiter.rb +7 -9
- data/lib/discordrb/container.rb +126 -26
- data/lib/discordrb/data.rb +264 -36
- data/lib/discordrb/events/generic.rb +18 -2
- data/lib/discordrb/events/guilds.rb +117 -2
- data/lib/discordrb/events/message.rb +7 -5
- data/lib/discordrb/events/presence.rb +4 -12
- data/lib/discordrb/events/raw.rb +49 -0
- data/lib/discordrb/events/reactions.rb +104 -0
- data/lib/discordrb/events/typing.rb +4 -2
- data/lib/discordrb/events/voice_state_update.rb +17 -2
- data/lib/discordrb/gateway.rb +75 -17
- data/lib/discordrb/permissions.rb +5 -3
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +2 -5
- data/lib/discordrb/voice/network.rb +2 -2
- data/lib/discordrb/voice/voice_bot.rb +5 -6
- data/lib/discordrb/webhooks.rb +12 -0
- metadata +35 -15
data/lib/discordrb/gateway.rb
CHANGED
@@ -72,8 +72,9 @@ module Discordrb
|
|
72
72
|
# members when it receives them. (Sending this is never necessary for a gateway client to behave correctly)
|
73
73
|
REQUEST_MEMBERS = 8
|
74
74
|
|
75
|
-
# **Received**:
|
76
|
-
#
|
75
|
+
# **Received**: Sent by Discord when the session becomes invalid for any reason. This may include improperly
|
76
|
+
# resuming existing sessions, attempting to start sessions with invalid data, or something else entirely. The client
|
77
|
+
# should handle this by simply starting a new session.
|
77
78
|
INVALIDATE_SESSION = 9
|
78
79
|
|
79
80
|
# **Received**: Sent immediately for any opened connection; tells the client to start heartbeating early on, so the
|
@@ -93,7 +94,7 @@ module Discordrb
|
|
93
94
|
attr_accessor :sequence
|
94
95
|
|
95
96
|
def initialize(session_id)
|
96
|
-
@
|
97
|
+
@session_id = session_id
|
97
98
|
@sequence = 0
|
98
99
|
@suspended = false
|
99
100
|
@invalid = false
|
@@ -107,6 +108,10 @@ module Discordrb
|
|
107
108
|
@suspended
|
108
109
|
end
|
109
110
|
|
111
|
+
def resume
|
112
|
+
@suspended = false
|
113
|
+
end
|
114
|
+
|
110
115
|
def invalidate
|
111
116
|
@invalid = true
|
112
117
|
end
|
@@ -128,14 +133,25 @@ module Discordrb
|
|
128
133
|
# The version of the gateway that's supposed to be used.
|
129
134
|
GATEWAY_VERSION = 6
|
130
135
|
|
131
|
-
|
136
|
+
# Heartbeat ACKs are Discord's way of verifying on the client side whether the connection is still alive. If this is
|
137
|
+
# set to true (default value) the gateway client will use that functionality to detect zombie connections and
|
138
|
+
# reconnect in such a case; however it may lead to instability if there's some problem with the ACKs. If this occurs
|
139
|
+
# it can simply be set to false.
|
140
|
+
# @return [true, false] whether or not this gateway should check for heartbeat ACKs.
|
141
|
+
attr_accessor :check_heartbeat_acks
|
142
|
+
|
143
|
+
def initialize(bot, token, shard_key = nil)
|
132
144
|
@token = token
|
133
145
|
@bot = bot
|
134
146
|
|
147
|
+
@shard_key = shard_key
|
148
|
+
|
135
149
|
@getc_mutex = Mutex.new
|
136
150
|
|
137
151
|
# Whether the connection to the gateway has succeeded yet
|
138
152
|
@ws_success = false
|
153
|
+
|
154
|
+
@check_heartbeat_acks = true
|
139
155
|
end
|
140
156
|
|
141
157
|
# Connect to the gateway server in a separate thread
|
@@ -213,6 +229,21 @@ module Discordrb
|
|
213
229
|
# before it), or if none have been received yet, with 0.
|
214
230
|
# @see #send_heartbeat
|
215
231
|
def heartbeat
|
232
|
+
if check_heartbeat_acks
|
233
|
+
unless @last_heartbeat_acked
|
234
|
+
# We're in a bad situation - apparently the last heartbeat wasn't acked, which means the connection is likely
|
235
|
+
# a zombie. Reconnect
|
236
|
+
LOGGER.warn('Last heartbeat was not acked, so this is a zombie connection! Reconnecting')
|
237
|
+
|
238
|
+
# We can't send anything on zombie connections
|
239
|
+
@pipe_broken = true
|
240
|
+
reconnect
|
241
|
+
return
|
242
|
+
end
|
243
|
+
|
244
|
+
@last_heartbeat_acked = false
|
245
|
+
end
|
246
|
+
|
216
247
|
send_heartbeat(@session ? @session.sequence : 0)
|
217
248
|
end
|
218
249
|
|
@@ -232,7 +263,7 @@ module Discordrb
|
|
232
263
|
:'$device' => 'discordrb',
|
233
264
|
:'$referrer' => '',
|
234
265
|
:'$referring_domain' => ''
|
235
|
-
}, true, 100)
|
266
|
+
}, true, 100, @shard_key)
|
236
267
|
end
|
237
268
|
|
238
269
|
# Sends an identify packet (op 2). This starts a new session on the current connection and tells Discord who we are.
|
@@ -251,7 +282,9 @@ module Discordrb
|
|
251
282
|
# @param compress [true, false] Whether certain large packets should be compressed using zlib.
|
252
283
|
# @param large_threshold [Integer] The member threshold after which a server counts as large and will have to have
|
253
284
|
# its member list chunked.
|
254
|
-
|
285
|
+
# @param shard_key [Array(Integer, Integer), nil] The shard key to use for sharding, represented as
|
286
|
+
# [shard_id, num_shards], or nil if the bot should not be sharded.
|
287
|
+
def send_identify(token, properties, compress, large_threshold, shard_key = nil)
|
255
288
|
data = {
|
256
289
|
# Don't send a v anymore as it's entirely determined by the URL now
|
257
290
|
token: token,
|
@@ -260,6 +293,9 @@ module Discordrb
|
|
260
293
|
large_threshold: large_threshold
|
261
294
|
}
|
262
295
|
|
296
|
+
# Don't include the shard key at all if it is nil as Discord checks for its mere existence
|
297
|
+
data[:shard] = shard_key if shard_key
|
298
|
+
|
263
299
|
send_packet(Opcodes::IDENTIFY, data)
|
264
300
|
end
|
265
301
|
|
@@ -305,6 +341,17 @@ module Discordrb
|
|
305
341
|
send_resume(@token, @session.session_id, @session.sequence)
|
306
342
|
end
|
307
343
|
|
344
|
+
# Reconnects the gateway connection in a controlled manner.
|
345
|
+
# @param attempt_resume [true, false] Whether a resume should be attempted after the reconnection.
|
346
|
+
def reconnect(attempt_resume = true)
|
347
|
+
@session.suspend if attempt_resume
|
348
|
+
|
349
|
+
@instant_reconnect = true
|
350
|
+
@should_reconnect = true
|
351
|
+
|
352
|
+
close
|
353
|
+
end
|
354
|
+
|
308
355
|
# Sends a resume packet (op 6). This replays all events from a previous point specified by its packet sequence. This
|
309
356
|
# will not work if the packet to resume from has already been acknowledged using a heartbeat, or if the session ID
|
310
357
|
# belongs to a now invalid session.
|
@@ -345,6 +392,12 @@ module Discordrb
|
|
345
392
|
private
|
346
393
|
|
347
394
|
def setup_heartbeats(interval)
|
395
|
+
# Make sure to reset ACK handling, so we don't keep reconnecting
|
396
|
+
@last_heartbeat_acked = true
|
397
|
+
|
398
|
+
# If we suspended the session before because of a reconnection, we need to resume it now
|
399
|
+
@session.resume if @session && @session.suspended?
|
400
|
+
|
348
401
|
# We don't want to have redundant heartbeat threads, so if one already exists, don't start a new one
|
349
402
|
return if @heartbeat_thread
|
350
403
|
|
@@ -381,8 +434,7 @@ module Discordrb
|
|
381
434
|
break unless @should_reconnect
|
382
435
|
|
383
436
|
if @instant_reconnect
|
384
|
-
|
385
|
-
LOGGER.info('Got an op 7, reconnecting right away')
|
437
|
+
LOGGER.info('Instant reconnection flag was set - reconnecting right away')
|
386
438
|
@instant_reconnect = false
|
387
439
|
else
|
388
440
|
wait_for_reconnect
|
@@ -488,6 +540,7 @@ module Discordrb
|
|
488
540
|
recv_data = nil
|
489
541
|
|
490
542
|
# Get some data from the socket, synchronised so the socket can't be closed during this
|
543
|
+
# 24: remove locking
|
491
544
|
@getc_mutex.synchronize { recv_data = @socket.getc }
|
492
545
|
|
493
546
|
# Check if we actually got data
|
@@ -533,8 +586,7 @@ module Discordrb
|
|
533
586
|
handle_open
|
534
587
|
end
|
535
588
|
|
536
|
-
def handle_open
|
537
|
-
end
|
589
|
+
def handle_open; end
|
538
590
|
|
539
591
|
def handle_error(e)
|
540
592
|
LOGGER.error('An error occurred in the main websocket loop!')
|
@@ -571,6 +623,8 @@ module Discordrb
|
|
571
623
|
handle_invalidate_session
|
572
624
|
when Opcodes::HEARTBEAT_ACK
|
573
625
|
handle_heartbeat_ack(packet)
|
626
|
+
when Opcodes::HEARTBEAT
|
627
|
+
handle_heartbeat(packet)
|
574
628
|
else
|
575
629
|
LOGGER.warn("Received invalid opcode #{op} - please report with this information: #{msg}")
|
576
630
|
end
|
@@ -591,20 +645,23 @@ module Discordrb
|
|
591
645
|
# The RESUMED event is received after a successful op 6 (resume). It does nothing except tell the bot the
|
592
646
|
# connection is initiated (like READY would). Starting with v5, it doesn't set a new heartbeat interval anymore
|
593
647
|
# since that is handled by op 10 (HELLO).
|
594
|
-
LOGGER.
|
648
|
+
LOGGER.good 'Resumed'
|
595
649
|
return
|
596
650
|
end
|
597
651
|
|
598
652
|
@bot.dispatch(type, data)
|
599
653
|
end
|
600
654
|
|
655
|
+
# Op 1
|
656
|
+
def handle_heartbeat(packet)
|
657
|
+
# If we receive a heartbeat, we have to resend one with the same sequence
|
658
|
+
send_heartbeat(packet['s'])
|
659
|
+
end
|
660
|
+
|
601
661
|
# Op 7
|
602
662
|
def handle_reconnect
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
# Suspend session so we resume afterwards
|
607
|
-
@session.suspend
|
663
|
+
LOGGER.debug('Received op 7, reconnecting and attempting resume')
|
664
|
+
reconnect
|
608
665
|
end
|
609
666
|
|
610
667
|
# Op 9
|
@@ -640,6 +697,7 @@ module Discordrb
|
|
640
697
|
# Op 11
|
641
698
|
def handle_heartbeat_ack(packet)
|
642
699
|
LOGGER.debug("Received heartbeat ack for packet: #{packet.inspect}")
|
700
|
+
@last_heartbeat_acked = true if @check_heartbeat_acks
|
643
701
|
end
|
644
702
|
|
645
703
|
# Called when the websocket has been disconnected in some way - say due to a pipe error while sending
|
@@ -686,7 +744,7 @@ module Discordrb
|
|
686
744
|
# Try to send it
|
687
745
|
begin
|
688
746
|
@socket.write frame.to_s
|
689
|
-
rescue
|
747
|
+
rescue => e
|
690
748
|
# There has been an error!
|
691
749
|
@pipe_broken = true
|
692
750
|
handle_internal_close(e)
|
@@ -13,7 +13,7 @@ module Discordrb
|
|
13
13
|
3 => :administrator, # 8
|
14
14
|
4 => :manage_channels, # 16
|
15
15
|
5 => :manage_server, # 32
|
16
|
-
|
16
|
+
6 => :add_reactions, # 64
|
17
17
|
# 7 # 128
|
18
18
|
# 8 # 256
|
19
19
|
# 9 # 512
|
@@ -35,7 +35,9 @@ module Discordrb
|
|
35
35
|
25 => :use_voice_activity, # 33554432
|
36
36
|
26 => :change_nickname, # 67108864
|
37
37
|
27 => :manage_nicknames, # 134217728
|
38
|
-
28 => :manage_roles
|
38
|
+
28 => :manage_roles, # 268435456, also Manage Permissions
|
39
|
+
29 => :manage_webhooks, # 536870912
|
40
|
+
30 => :manage_emojis # 1073741824
|
39
41
|
}.freeze
|
40
42
|
|
41
43
|
Flags.each do |position, flag|
|
@@ -58,7 +60,7 @@ module Discordrb
|
|
58
60
|
attr_reader :bits
|
59
61
|
|
60
62
|
# Set the raw bitset of this permission object
|
61
|
-
# @param bits [
|
63
|
+
# @param bits [Integer] A number whose binary representation is the desired bitset.
|
62
64
|
def bits=(bits)
|
63
65
|
@bits = bits
|
64
66
|
init_vars
|
data/lib/discordrb/version.rb
CHANGED
@@ -29,11 +29,8 @@ module Discordrb::Voice
|
|
29
29
|
@channels = 2
|
30
30
|
@filter_volume = 1
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
else
|
35
|
-
raise LoadError, 'Opus unavailable - voice not supported! Please install opus for voice support to work.'
|
36
|
-
end
|
32
|
+
raise LoadError, 'Opus unavailable - voice not supported! Please install opus for voice support to work.' unless OPUS_AVAILABLE
|
33
|
+
@opus = Opus::Encoder.new(@sample_rate, @frame_size, @channels)
|
37
34
|
end
|
38
35
|
|
39
36
|
# Set the opus encoding bitrate
|
@@ -303,8 +303,8 @@ module Discordrb::Voice
|
|
303
303
|
host,
|
304
304
|
method(:websocket_open),
|
305
305
|
method(:websocket_message),
|
306
|
-
proc { |e| Discordrb::
|
307
|
-
proc { |e| Discordrb::
|
306
|
+
proc { |e| Discordrb::LOGGER.error "VWS error: #{e}" },
|
307
|
+
proc { |e| Discordrb::LOGGER.warn "VWS close: #{e}" }
|
308
308
|
)
|
309
309
|
|
310
310
|
@bot.debug('VWS connected')
|
@@ -160,11 +160,10 @@ module Discordrb::Voice
|
|
160
160
|
@playing = false
|
161
161
|
sleep IDEAL_LENGTH / 1000.0 if @was_playing_before
|
162
162
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
end
|
163
|
+
return unless wait_for_confirmation
|
164
|
+
@has_stopped_playing = false
|
165
|
+
sleep IDEAL_LENGTH / 1000.0 until @has_stopped_playing
|
166
|
+
@has_stopped_playing = false
|
168
167
|
end
|
169
168
|
|
170
169
|
# Permanently disconnects from the voice channel; to reconnect you will have to call {Bot#voice_connect} again.
|
@@ -364,7 +363,7 @@ module Discordrb::Voice
|
|
364
363
|
# Wait `length` ms, then send the next packet
|
365
364
|
sleep @length / 1000.0
|
366
365
|
else
|
367
|
-
Discordrb::
|
366
|
+
Discordrb::LOGGER.warn('Audio encoding and sending together took longer than Discord expects one packet to be (20 ms)! This may be indicative of network problems.')
|
368
367
|
end
|
369
368
|
end
|
370
369
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'discordrb/webhooks/version'
|
4
|
+
require 'discordrb/webhooks/embeds'
|
5
|
+
require 'discordrb/webhooks/client'
|
6
|
+
require 'discordrb/webhooks/builder'
|
7
|
+
|
8
|
+
module Discordrb
|
9
|
+
# Webhook client
|
10
|
+
module Webhooks
|
11
|
+
end
|
12
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: discordrb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- meew0
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: opus-ruby
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
@@ -39,35 +39,35 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: websocket-client-simple
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.3.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.3.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rbnacl
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 3.4.0
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 3.4.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: discordrb-webhooks
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
@@ -136,20 +136,34 @@ dependencies:
|
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: 3.4.0
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rspec-prof
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.0.7
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.0.7
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
154
|
name: rubocop
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
142
156
|
requirements:
|
143
157
|
- - '='
|
144
158
|
- !ruby/object:Gem::Version
|
145
|
-
version: 0.
|
159
|
+
version: 0.45.0
|
146
160
|
type: :development
|
147
161
|
prerelease: false
|
148
162
|
version_requirements: !ruby/object:Gem::Requirement
|
149
163
|
requirements:
|
150
164
|
- - '='
|
151
165
|
- !ruby/object:Gem::Version
|
152
|
-
version: 0.
|
166
|
+
version: 0.45.0
|
153
167
|
description: A Ruby implementation of the Discord (https://discordapp.com) API.
|
154
168
|
email:
|
155
169
|
- ''
|
@@ -157,6 +171,7 @@ executables: []
|
|
157
171
|
extensions: []
|
158
172
|
extra_rdoc_files: []
|
159
173
|
files:
|
174
|
+
- ".codeclimate.yml"
|
160
175
|
- ".gitignore"
|
161
176
|
- ".overcommit.yml"
|
162
177
|
- ".rspec"
|
@@ -164,12 +179,14 @@ files:
|
|
164
179
|
- ".travis.yml"
|
165
180
|
- ".yardopts"
|
166
181
|
- CHANGELOG.md
|
182
|
+
- CONTRIBUTING.md
|
167
183
|
- Gemfile
|
168
184
|
- LICENSE.txt
|
169
185
|
- README.md
|
170
186
|
- Rakefile
|
171
187
|
- bin/console
|
172
188
|
- bin/setup
|
189
|
+
- discordrb-webhooks.gemspec
|
173
190
|
- discordrb.gemspec
|
174
191
|
- lib/discordrb.rb
|
175
192
|
- lib/discordrb/api.rb
|
@@ -197,6 +214,8 @@ files:
|
|
197
214
|
- lib/discordrb/events/members.rb
|
198
215
|
- lib/discordrb/events/message.rb
|
199
216
|
- lib/discordrb/events/presence.rb
|
217
|
+
- lib/discordrb/events/raw.rb
|
218
|
+
- lib/discordrb/events/reactions.rb
|
200
219
|
- lib/discordrb/events/roles.rb
|
201
220
|
- lib/discordrb/events/typing.rb
|
202
221
|
- lib/discordrb/events/voice_state_update.rb
|
@@ -211,6 +230,7 @@ files:
|
|
211
230
|
- lib/discordrb/voice/encoder.rb
|
212
231
|
- lib/discordrb/voice/network.rb
|
213
232
|
- lib/discordrb/voice/voice_bot.rb
|
233
|
+
- lib/discordrb/webhooks.rb
|
214
234
|
- lib/discordrb/websocket.rb
|
215
235
|
homepage: https://github.com/meew0/discordrb
|
216
236
|
licenses:
|
@@ -232,7 +252,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
232
252
|
version: '0'
|
233
253
|
requirements: []
|
234
254
|
rubyforge_project:
|
235
|
-
rubygems_version: 2.
|
255
|
+
rubygems_version: 2.6.10
|
236
256
|
signing_key:
|
237
257
|
specification_version: 4
|
238
258
|
summary: Discord API for Ruby
|