discordrb 1.6.6 → 1.7.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/.travis.yml +3 -0
- data/CHANGELOG.md +26 -0
- data/README.md +1 -1
- data/discordrb.gemspec +2 -0
- data/lib/discordrb.rb +1 -1
- data/lib/discordrb/api.rb +36 -5
- data/lib/discordrb/await.rb +3 -1
- data/lib/discordrb/bot.rb +70 -275
- data/lib/discordrb/commands/command_bot.rb +81 -28
- data/lib/discordrb/commands/container.rb +78 -0
- data/lib/discordrb/commands/events.rb +2 -12
- data/lib/discordrb/commands/parser.rb +40 -4
- data/lib/discordrb/commands/rate_limiter.rb +141 -0
- data/lib/discordrb/container.rb +379 -0
- data/lib/discordrb/data.rb +52 -4
- data/lib/discordrb/errors.rb +24 -0
- data/lib/discordrb/events/bans.rb +10 -8
- data/lib/discordrb/events/generic.rb +38 -2
- data/lib/discordrb/events/guilds.rb +11 -1
- data/lib/discordrb/events/lifetime.rb +6 -0
- data/lib/discordrb/events/members.rb +9 -1
- data/lib/discordrb/events/message.rb +33 -1
- data/lib/discordrb/logger.rb +7 -1
- data/lib/discordrb/permissions.rb +3 -0
- data/lib/discordrb/token_cache.rb +38 -4
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +29 -19
- data/lib/discordrb/voice/network.rb +6 -11
- data/lib/discordrb/voice/voice_bot.rb +112 -43
- metadata +34 -3
- data/lib/discordrb/exceptions.rb +0 -12
data/lib/discordrb/version.rb
CHANGED
@@ -11,19 +11,8 @@ module Discordrb::Voice
|
|
11
11
|
# This class conveniently abstracts opus and ffmpeg/avconv, for easy implementation of voice sending. It's not very
|
12
12
|
# useful for most users, but I guess it can be useful sometimes.
|
13
13
|
class Encoder
|
14
|
-
# The volume that should be used with future ffmpeg conversions. If ffmpeg is used, this can be specified as:
|
15
|
-
#
|
16
|
-
# * A number, where `1` is no change in volume, `0` is completely silent, `0.5` is half the default volume and `2` is twice the default.
|
17
|
-
# * A string representation of the above number.
|
18
|
-
# * A string representing a change in gain given in decibels, in the format `-6dB` or `6dB`.
|
19
|
-
#
|
20
|
-
# If avconv is used (see #use_avconv) then it can only be given as a number from `0` to `1`, where `1` is no change
|
21
|
-
# and `0` is completely silent.
|
22
|
-
# @return [String, Number] the volume for future playbacks, `1.0` by default.
|
23
|
-
attr_accessor :volume
|
24
|
-
|
25
14
|
# Whether or not avconv should be used instead of ffmpeg. If possible, it is recommended to use ffmpeg instead,
|
26
|
-
# as it is better supported
|
15
|
+
# as it is better supported.
|
27
16
|
# @return [true, false] whether avconv should be used instead of ffmpeg.
|
28
17
|
attr_accessor :use_avconv
|
29
18
|
|
@@ -32,7 +21,6 @@ module Discordrb::Voice
|
|
32
21
|
@sample_rate = 48_000
|
33
22
|
@frame_size = 960
|
34
23
|
@channels = 2
|
35
|
-
@volume = 1.0
|
36
24
|
|
37
25
|
if OPUS_AVAILABLE
|
38
26
|
@opus = Opus::Encoder.new(@sample_rate, @frame_size, @channels)
|
@@ -41,6 +29,12 @@ module Discordrb::Voice
|
|
41
29
|
end
|
42
30
|
end
|
43
31
|
|
32
|
+
# Set the opus encoding bitrate
|
33
|
+
# @param value [Integer] The new bitrate to use, in bits per second (so 64000 if you want 64 kbps)
|
34
|
+
def bitrate=(value)
|
35
|
+
@opus.bitrate = value
|
36
|
+
end
|
37
|
+
|
44
38
|
# Encodes the given buffer using opus.
|
45
39
|
# @param buffer [String] An unencoded PCM (s16le) buffer.
|
46
40
|
# @return [String] A buffer encoded using opus.
|
@@ -53,13 +47,33 @@ module Discordrb::Voice
|
|
53
47
|
@opus.destroy
|
54
48
|
end
|
55
49
|
|
50
|
+
# Adjusts the volume of a given buffer of s16le PCM data.
|
51
|
+
# @param buf [String] An unencoded PCM (s16le) buffer.
|
52
|
+
# @param mult [Float] The volume multiplier, 1 for same volume.
|
53
|
+
# @return [String] The buffer with adjusted volume, s16le again
|
54
|
+
def adjust_volume(buf, mult)
|
55
|
+
# We don't need to adjust anything if the buf is nil so just return in that case
|
56
|
+
return unless buf
|
57
|
+
|
58
|
+
# buf is s16le so use 's<' for signed, 16 bit, LE
|
59
|
+
result = buf.unpack('s<*').map do |sample|
|
60
|
+
sample *= mult
|
61
|
+
|
62
|
+
# clamp to s16 range
|
63
|
+
[32_767, [-32_768, sample].max].min
|
64
|
+
end
|
65
|
+
|
66
|
+
# After modification, make it s16le again
|
67
|
+
result.pack('s<*')
|
68
|
+
end
|
69
|
+
|
56
70
|
# Encodes a given file (or rather, decodes it) using ffmpeg. This accepts pretty much any format, even videos with
|
57
71
|
# an audio track. For a list of supported formats, see https://ffmpeg.org/general.html#Audio-Codecs. It even accepts
|
58
72
|
# URLs, though encoding them is pretty slow - I recommend to make a stream of it and then use {#encode_io} instead.
|
59
73
|
# @param file [String] The path or URL to encode.
|
60
74
|
# @return [IO] the audio, encoded as s16le PCM
|
61
75
|
def encode_file(file)
|
62
|
-
command = "#{ffmpeg_command} -loglevel 0 -i \"#{file}\" -f s16le -ar 48000 -ac 2
|
76
|
+
command = "#{ffmpeg_command} -loglevel 0 -i \"#{file}\" -f s16le -ar 48000 -ac 2 pipe:1"
|
63
77
|
IO.popen(command)
|
64
78
|
end
|
65
79
|
|
@@ -69,7 +83,7 @@ module Discordrb::Voice
|
|
69
83
|
# @return [IO] the audio, encoded as s16le PCM
|
70
84
|
def encode_io(io)
|
71
85
|
ret_io, writer = IO.pipe
|
72
|
-
command = "#{ffmpeg_command} -loglevel 0 -i - -f s16le -ar 48000 -ac 2
|
86
|
+
command = "#{ffmpeg_command} -loglevel 0 -i - -f s16le -ar 48000 -ac 2 pipe:1"
|
73
87
|
spawn(command, in: io, out: writer)
|
74
88
|
ret_io
|
75
89
|
end
|
@@ -79,9 +93,5 @@ module Discordrb::Voice
|
|
79
93
|
def ffmpeg_command
|
80
94
|
@use_avconv ? 'avconv' : 'ffmpeg'
|
81
95
|
end
|
82
|
-
|
83
|
-
def ffmpeg_volume
|
84
|
-
@use_avconv ? "-vol #{(@volume * 256).ceil}" : "-af volume=#{@volume}"
|
85
|
-
end
|
86
96
|
end
|
87
97
|
end
|
@@ -5,20 +5,19 @@ require 'json'
|
|
5
5
|
|
6
6
|
begin
|
7
7
|
require 'rbnacl'
|
8
|
-
|
8
|
+
RBNACL_AVAILABLE = true
|
9
9
|
rescue LoadError
|
10
10
|
puts "libsodium not available! You can continue to use discordrb as normal but voice support won't work.
|
11
11
|
Read https://github.com/meew0/discordrb/wiki/Installing-libsodium for more details."
|
12
|
-
|
12
|
+
RBNACL_AVAILABLE = false
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
15
|
module Discordrb::Voice
|
17
16
|
# Signifies to Discord that encryption should be used
|
18
|
-
ENCRYPTED_MODE = 'xsalsa20_poly1305'
|
17
|
+
ENCRYPTED_MODE = 'xsalsa20_poly1305'.freeze
|
19
18
|
|
20
19
|
# Signifies to Discord that no encryption should be used
|
21
|
-
PLAIN_MODE = 'plain'
|
20
|
+
PLAIN_MODE = 'plain'.freeze
|
22
21
|
|
23
22
|
# Represents a UDP connection to a voice server. This connection is used to send the actual audio data.
|
24
23
|
class VoiceUDP
|
@@ -70,9 +69,7 @@ module Discordrb::Voice
|
|
70
69
|
header = [0x80, 0x78, sequence, time, @ssrc].pack('CCnNN')
|
71
70
|
|
72
71
|
# Encrypt data, if necessary
|
73
|
-
if encrypted?
|
74
|
-
buf = encrypt_audio(header, buf)
|
75
|
-
end
|
72
|
+
buf = encrypt_audio(header, buf) if encrypted?
|
76
73
|
|
77
74
|
send_packet(header + buf)
|
78
75
|
end
|
@@ -122,7 +119,7 @@ module Discordrb::Voice
|
|
122
119
|
# @param session [String] The voice session ID Discord sends over the regular websocket
|
123
120
|
# @param endpoint [String] The endpoint URL to connect to
|
124
121
|
def initialize(channel, bot, token, session, endpoint)
|
125
|
-
fail 'RbNaCl is unavailable - unable to create voice bot! Please read https://github.com/meew0/discordrb/wiki/Installing-libsodium' unless
|
122
|
+
fail 'RbNaCl is unavailable - unable to create voice bot! Please read https://github.com/meew0/discordrb/wiki/Installing-libsodium' unless RBNACL_AVAILABLE
|
126
123
|
|
127
124
|
@channel = channel
|
128
125
|
@bot = bot
|
@@ -223,8 +220,6 @@ module Discordrb::Voice
|
|
223
220
|
@ws_data = packet['d']
|
224
221
|
@ready = true
|
225
222
|
@udp.secret_key = @ws_data['secret_key'].pack('C*')
|
226
|
-
else
|
227
|
-
# irrelevant opcode, ignore
|
228
223
|
end
|
229
224
|
end
|
230
225
|
|
@@ -48,6 +48,25 @@ module Discordrb::Voice
|
|
48
48
|
# @return [true, false] whether adjustment lengths should be averaged with the respective previous value.
|
49
49
|
attr_accessor :adjust_average
|
50
50
|
|
51
|
+
# Disable the debug message for length adjustment specifically, as it can get quite spammy with very low intervals
|
52
|
+
# @see #adjust_interval
|
53
|
+
# @return [true, false] whether length adjustment debug messages should be printed
|
54
|
+
attr_accessor :adjust_debug
|
55
|
+
|
56
|
+
# If this value is set, no length adjustments will ever be done and this value will always be used as the length
|
57
|
+
# (i. e. packets will be sent every N seconds). Be careful not to set it too low as to not spam Discord's servers.
|
58
|
+
# The ideal length is 20 ms (accessible by the {Discordrb::Voice::IDEAL_LENGTH} constant), this value should be
|
59
|
+
# slightly lower than that because encoding + sending takes time. Note that sending DCA files is significantly
|
60
|
+
# faster than sending regular audio files (usually about four times as fast), so you might want to set this value
|
61
|
+
# to something else if you're sending a DCA file.
|
62
|
+
# @return [Float] the packet length that should be used instead of calculating it during the adjustments, in ms.
|
63
|
+
attr_accessor :length_override
|
64
|
+
|
65
|
+
# The factor the audio's volume should be multiplied with. `1` is no change in volume, `0` is completely silent,
|
66
|
+
# `0.5` is half the default volume and `2` is twice the default.
|
67
|
+
# @return [Float] the volume for audio playback, `1.0` by default.
|
68
|
+
attr_accessor :volume
|
69
|
+
|
51
70
|
def initialize(channel, bot, token, session, endpoint, encrypted)
|
52
71
|
@bot = bot
|
53
72
|
@ws = VoiceWS.new(channel, bot, token, session, endpoint)
|
@@ -55,27 +74,19 @@ module Discordrb::Voice
|
|
55
74
|
@udp.encrypted = encrypted
|
56
75
|
|
57
76
|
@sequence = @time = 0
|
77
|
+
@skips = 0
|
58
78
|
|
59
79
|
@adjust_interval = 100
|
60
80
|
@adjust_offset = 10
|
61
81
|
@adjust_average = false
|
82
|
+
@adjust_debug = true
|
83
|
+
|
84
|
+
@volume = 1.0
|
62
85
|
|
63
86
|
@encoder = Encoder.new
|
64
87
|
@ws.connect
|
65
88
|
end
|
66
89
|
|
67
|
-
# Set the volume. Only applies to future playbacks
|
68
|
-
# @see Encoder#volume=
|
69
|
-
def volume=(value)
|
70
|
-
@encoder.volume = value
|
71
|
-
end
|
72
|
-
|
73
|
-
# @see Encoder#volume
|
74
|
-
# @return [Integer, String] the current encoder volume.
|
75
|
-
def volume
|
76
|
-
@encoder.volume
|
77
|
-
end
|
78
|
-
|
79
90
|
# @return [true, false] whether audio data sent will be encrypted.
|
80
91
|
def encrypted?
|
81
92
|
@udp.encrypted?
|
@@ -92,6 +103,13 @@ module Discordrb::Voice
|
|
92
103
|
@paused = false
|
93
104
|
end
|
94
105
|
|
106
|
+
# Skips to a later time in the song. It's impossible to go back without replaying the song.
|
107
|
+
# @param secs [Float] How many seconds to skip forwards. Skipping will always be done in discrete intervals of
|
108
|
+
# 0.05 seconds, so if the given amount is smaller than that, it will be rounded up.
|
109
|
+
def skip(secs)
|
110
|
+
@skips += (secs * (1000 / IDEAL_LENGTH)).ceil
|
111
|
+
end
|
112
|
+
|
95
113
|
# Sets whether or not the bot is speaking (green circle around user).
|
96
114
|
# @param value [true, false] whether or not the bot should be speaking.
|
97
115
|
def speaking=(value)
|
@@ -103,8 +121,7 @@ module Discordrb::Voice
|
|
103
121
|
def stop_playing
|
104
122
|
@was_playing_before = @playing
|
105
123
|
@speaking = false
|
106
|
-
@
|
107
|
-
@io = nil
|
124
|
+
@playing = false
|
108
125
|
sleep IDEAL_LENGTH / 1000.0 if @was_playing_before
|
109
126
|
end
|
110
127
|
|
@@ -122,8 +139,34 @@ module Discordrb::Voice
|
|
122
139
|
# @param encoded_io [IO] A stream of raw PCM data (s16le)
|
123
140
|
def play(encoded_io)
|
124
141
|
stop_playing if @playing
|
125
|
-
@
|
126
|
-
|
142
|
+
@retry_attempts = 3
|
143
|
+
|
144
|
+
play_internal do
|
145
|
+
buf = nil
|
146
|
+
|
147
|
+
# Read some data from the buffer
|
148
|
+
begin
|
149
|
+
buf = encoded_io.readpartial(DATA_LENGTH) if encoded_io
|
150
|
+
rescue EOFError
|
151
|
+
@bot.debug('EOF while reading, breaking immediately')
|
152
|
+
break
|
153
|
+
end
|
154
|
+
|
155
|
+
# Check whether the buffer has enough data
|
156
|
+
if !buf || buf.length != DATA_LENGTH
|
157
|
+
@bot.debug("No data is available! Retrying #{@retry_attempts} more times")
|
158
|
+
break if @retry_attempts == 0
|
159
|
+
|
160
|
+
@retry_attempts -= 1
|
161
|
+
next
|
162
|
+
end
|
163
|
+
|
164
|
+
# Adjust volume
|
165
|
+
buf = @encoder.adjust_volume(buf, @volume) if @volume != 1.0
|
166
|
+
|
167
|
+
# Encode data
|
168
|
+
@encoder.encode(buf)
|
169
|
+
end
|
127
170
|
end
|
128
171
|
|
129
172
|
# Plays an encoded audio file of arbitrary format to the channel.
|
@@ -140,6 +183,31 @@ module Discordrb::Voice
|
|
140
183
|
play @encoder.encode_io(io)
|
141
184
|
end
|
142
185
|
|
186
|
+
# Plays a stream of audio data in the DCA format. This format has the advantage that no recoding has to be
|
187
|
+
# done - the file contains the data exactly as Discord needs it.
|
188
|
+
# @see https://github.com/bwmarrin/dca
|
189
|
+
# @see #play
|
190
|
+
def play_dca(file)
|
191
|
+
stop_playing if @playing
|
192
|
+
|
193
|
+
@bot.debug "Reading DCA file #{file}"
|
194
|
+
input_stream = open(file)
|
195
|
+
|
196
|
+
# Play the data, without re-encoding it to opus
|
197
|
+
play_internal do
|
198
|
+
begin
|
199
|
+
# Read header
|
200
|
+
header = input_stream.read(2).unpack('S')[0]
|
201
|
+
rescue EOFError
|
202
|
+
@bot.debug 'Finished DCA parsing'
|
203
|
+
break
|
204
|
+
end
|
205
|
+
|
206
|
+
# Read bytes
|
207
|
+
input_stream.read(header)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
143
211
|
alias_method :play_stream, :play_io
|
144
212
|
|
145
213
|
private
|
@@ -148,36 +216,24 @@ module Discordrb::Voice
|
|
148
216
|
def play_internal
|
149
217
|
count = 0
|
150
218
|
@playing = true
|
151
|
-
@retry_attempts = 3
|
152
219
|
|
153
220
|
# Default play length (ms), will be adjusted later
|
154
221
|
@length = IDEAL_LENGTH
|
155
222
|
|
156
223
|
self.speaking = true
|
157
224
|
loop do
|
158
|
-
|
159
|
-
|
160
|
-
@length_adjust = Time.now.nsec
|
161
|
-
end
|
225
|
+
# Starting from the tenth packet, perform length adjustment every 100 packets (2 seconds)
|
226
|
+
should_adjust_this_packet = (count % @adjust_interval == @adjust_offset)
|
162
227
|
|
163
|
-
|
164
|
-
|
228
|
+
# If we should adjust, start now
|
229
|
+
@length_adjust = Time.now.nsec if should_adjust_this_packet
|
165
230
|
|
166
|
-
|
167
|
-
buf = nil
|
168
|
-
begin
|
169
|
-
buf = @io.readpartial(DATA_LENGTH) if @io
|
170
|
-
rescue EOFError
|
171
|
-
@bot.debug('EOF while reading, breaking immediately')
|
172
|
-
break
|
173
|
-
end
|
174
|
-
|
175
|
-
# Check whether the buffer has enough data
|
176
|
-
if !buf || buf.length != DATA_LENGTH
|
177
|
-
@bot.debug("No data is available! Retrying #{@retry_attempts} more times")
|
178
|
-
break if @retry_attempts == 0
|
231
|
+
break unless @playing
|
179
232
|
|
180
|
-
|
233
|
+
# If we should skip, get some data, discard it and go to the next iteration
|
234
|
+
if @skips > 0
|
235
|
+
@skips -= 1
|
236
|
+
yield
|
181
237
|
next
|
182
238
|
end
|
183
239
|
|
@@ -186,16 +242,27 @@ module Discordrb::Voice
|
|
186
242
|
(@sequence + 10 < 65_535) ? @sequence += 1 : @sequence = 0
|
187
243
|
(@time + 9600 < 4_294_967_295) ? @time += 960 : @time = 0
|
188
244
|
|
189
|
-
#
|
190
|
-
|
245
|
+
# Get packet data
|
246
|
+
buf = yield
|
247
|
+
next unless buf
|
248
|
+
|
249
|
+
# Track intermediate adjustment so we can measure how much encoding contributes to the total time
|
250
|
+
@intermediate_adjust = Time.now.nsec if should_adjust_this_packet
|
251
|
+
|
252
|
+
# Send the packet
|
253
|
+
@udp.send_audio(buf, @sequence, @time)
|
191
254
|
|
192
255
|
# Set the stream time (for tracking how long we've been playing)
|
193
256
|
@stream_time = count * @length / 1000
|
194
257
|
|
195
|
-
#
|
196
|
-
|
258
|
+
if @length_override # Don't do adjustment because the user has manually specified an override value
|
259
|
+
@length = @length_override
|
260
|
+
elsif @length_adjust # Perform length adjustment
|
261
|
+
# Define the time once so it doesn't get inaccurate
|
262
|
+
now = Time.now.nsec
|
263
|
+
|
197
264
|
# Difference between length_adjust and now in ms
|
198
|
-
ms_diff = (
|
265
|
+
ms_diff = (now - @length_adjust) / 1_000_000.0
|
199
266
|
if ms_diff >= 0
|
200
267
|
@length = if @adjust_average
|
201
268
|
(IDEAL_LENGTH - ms_diff + @length) / 2.0
|
@@ -203,7 +270,9 @@ module Discordrb::Voice
|
|
203
270
|
IDEAL_LENGTH - ms_diff
|
204
271
|
end
|
205
272
|
|
206
|
-
|
273
|
+
# Track the time it took to encode
|
274
|
+
encode_ms = (@intermediate_adjust - @length_adjust) / 1_000_000.0
|
275
|
+
@bot.debug("Length adjustment: new length #{@length} (measured #{ms_diff}, #{(100 * encode_ms) / ms_diff}% encoding)") if @adjust_debug
|
207
276
|
end
|
208
277
|
@length_adjust = nil
|
209
278
|
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: 1.
|
4
|
+
version: 1.7.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-02-
|
11
|
+
date: 2016-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faye-websocket
|
@@ -136,6 +136,34 @@ dependencies:
|
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: 0.8.7.6
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rspec
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 3.4.0
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 3.4.0
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rubocop
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: 0.36.0
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: 0.36.0
|
139
167
|
description: A Ruby implementation of the Discord (https://discordapp.com) API.
|
140
168
|
email:
|
141
169
|
- ''
|
@@ -164,9 +192,13 @@ files:
|
|
164
192
|
- lib/discordrb/await.rb
|
165
193
|
- lib/discordrb/bot.rb
|
166
194
|
- lib/discordrb/commands/command_bot.rb
|
195
|
+
- lib/discordrb/commands/container.rb
|
167
196
|
- lib/discordrb/commands/events.rb
|
168
197
|
- lib/discordrb/commands/parser.rb
|
198
|
+
- lib/discordrb/commands/rate_limiter.rb
|
199
|
+
- lib/discordrb/container.rb
|
169
200
|
- lib/discordrb/data.rb
|
201
|
+
- lib/discordrb/errors.rb
|
170
202
|
- lib/discordrb/events/await.rb
|
171
203
|
- lib/discordrb/events/bans.rb
|
172
204
|
- lib/discordrb/events/channel_create.rb
|
@@ -183,7 +215,6 @@ files:
|
|
183
215
|
- lib/discordrb/events/presence.rb
|
184
216
|
- lib/discordrb/events/typing.rb
|
185
217
|
- lib/discordrb/events/voice_state_update.rb
|
186
|
-
- lib/discordrb/exceptions.rb
|
187
218
|
- lib/discordrb/logger.rb
|
188
219
|
- lib/discordrb/permissions.rb
|
189
220
|
- lib/discordrb/token_cache.rb
|