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.

@@ -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**: The functionality of this opcode is less known than the others but it appears to specifically
76
- # tell the client to invalidate its local session and continue by {IDENTIFY}ing.
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
- @id = session_id
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
- def initialize(bot, token)
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
- def send_identify(token, properties, compress, large_threshold)
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
- # We got an op 7! Don't wait before reconnecting
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.debug('Connection resumed')
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
- @instant_reconnect = true
604
- close
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 Errno::EPIPE => e
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
- # 6 # 64
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 # 268435456, also Manage Permissions
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 [Fixnum] A number whose binary representation is the desired bitset.
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
@@ -3,5 +3,5 @@
3
3
  # Discordrb and all its functionality, in this case only the version.
4
4
  module Discordrb
5
5
  # The current version of discordrb.
6
- VERSION = '3.1.1'.freeze
6
+ VERSION = '3.2.0'.freeze
7
7
  end
@@ -29,11 +29,8 @@ module Discordrb::Voice
29
29
  @channels = 2
30
30
  @filter_volume = 1
31
31
 
32
- if OPUS_AVAILABLE
33
- @opus = Opus::Encoder.new(@sample_rate, @frame_size, @channels)
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::Logger.error "VWS error: #{e}" },
307
- proc { |e| Discordrb::Logger.warn "VWS close: #{e}" }
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
- if wait_for_confirmation
164
- @has_stopped_playing = false
165
- sleep IDEAL_LENGTH / 1000.0 until @has_stopped_playing
166
- @has_stopped_playing = false
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::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.')
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.1.1
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: 2016-10-21 00:00:00.000000000 Z
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: activesupport
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: opus-ruby
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: '0'
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: '0'
54
+ version: 0.3.0
55
55
  - !ruby/object:Gem::Dependency
56
- name: websocket-client-simple
56
+ name: rbnacl
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.3.0
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: 0.3.0
68
+ version: 3.4.0
69
69
  - !ruby/object:Gem::Dependency
70
- name: rbnacl
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.42.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.42.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.5.1
255
+ rubygems_version: 2.6.10
236
256
  signing_key:
237
257
  specification_version: 4
238
258
  summary: Discord API for Ruby