discord.js-selfv13 13.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of discord.js-selfv13 might be problematic. Click here for more details.

Files changed (188) hide show
  1. package/.dccache +1 -0
  2. package/LICENSE +190 -0
  3. package/README.md +87 -0
  4. package/browser.js +9 -0
  5. package/deploy/deploy-key.enc +0 -0
  6. package/deploy/deploy.sh +90 -0
  7. package/deploy/test.sh +34 -0
  8. package/docs/README.md +1 -0
  9. package/docs/examples/attachments.md +163 -0
  10. package/docs/examples/avatars.js +29 -0
  11. package/docs/examples/embed.js +38 -0
  12. package/docs/examples/greeting.js +30 -0
  13. package/docs/examples/moderation.md +145 -0
  14. package/docs/examples/ping.js +29 -0
  15. package/docs/examples/webhook.js +12 -0
  16. package/docs/general/faq.md +23 -0
  17. package/docs/general/updating.md +181 -0
  18. package/docs/general/welcome.md +95 -0
  19. package/docs/index.yml +30 -0
  20. package/docs/logo.svg +19 -0
  21. package/docs/topics/voice.md +113 -0
  22. package/docs/topics/web.md +38 -0
  23. package/package.json +147 -0
  24. package/src/client/Client.js +564 -0
  25. package/src/client/ClientDataManager.js +150 -0
  26. package/src/client/ClientDataResolver.js +376 -0
  27. package/src/client/ClientManager.js +74 -0
  28. package/src/client/WebhookClient.js +118 -0
  29. package/src/client/actions/Action.js +23 -0
  30. package/src/client/actions/ActionsManager.js +40 -0
  31. package/src/client/actions/ChannelCreate.js +11 -0
  32. package/src/client/actions/ChannelDelete.js +30 -0
  33. package/src/client/actions/ChannelUpdate.js +74 -0
  34. package/src/client/actions/GuildBanRemove.js +13 -0
  35. package/src/client/actions/GuildChannelsPositionUpdate.js +19 -0
  36. package/src/client/actions/GuildDelete.js +57 -0
  37. package/src/client/actions/GuildEmojiCreate.js +17 -0
  38. package/src/client/actions/GuildEmojiDelete.js +18 -0
  39. package/src/client/actions/GuildEmojiUpdate.js +17 -0
  40. package/src/client/actions/GuildEmojisUpdate.js +38 -0
  41. package/src/client/actions/GuildMemberGet.js +10 -0
  42. package/src/client/actions/GuildMemberRemove.js +41 -0
  43. package/src/client/actions/GuildRoleCreate.js +26 -0
  44. package/src/client/actions/GuildRoleDelete.js +42 -0
  45. package/src/client/actions/GuildRoleUpdate.js +41 -0
  46. package/src/client/actions/GuildRolesPositionUpdate.js +19 -0
  47. package/src/client/actions/GuildSync.js +29 -0
  48. package/src/client/actions/GuildUpdate.js +34 -0
  49. package/src/client/actions/MessageCreate.js +53 -0
  50. package/src/client/actions/MessageDelete.js +35 -0
  51. package/src/client/actions/MessageDeleteBulk.js +26 -0
  52. package/src/client/actions/MessageReactionAdd.js +37 -0
  53. package/src/client/actions/MessageReactionRemove.js +37 -0
  54. package/src/client/actions/MessageReactionRemoveAll.js +25 -0
  55. package/src/client/actions/MessageUpdate.js +40 -0
  56. package/src/client/actions/Ready.js +1 -0
  57. package/src/client/actions/Ready.js.bak +65 -0
  58. package/src/client/actions/UserGet.js +11 -0
  59. package/src/client/actions/UserNoteUpdate.js +30 -0
  60. package/src/client/actions/UserUpdate.js +33 -0
  61. package/src/client/rest/APIRequest.js +56 -0
  62. package/src/client/rest/DiscordAPIError.js +60 -0
  63. package/src/client/rest/RESTManager.js +58 -0
  64. package/src/client/rest/RESTMethods.js +1006 -0
  65. package/src/client/rest/RequestHandlers/Burst.js +90 -0
  66. package/src/client/rest/RequestHandlers/RequestHandler.js +54 -0
  67. package/src/client/rest/RequestHandlers/Sequential.js +132 -0
  68. package/src/client/rest/UserAgentManager.js +25 -0
  69. package/src/client/voice/ClientVoiceManager.js +81 -0
  70. package/src/client/voice/VoiceBroadcast.js +366 -0
  71. package/src/client/voice/VoiceConnection.js +530 -0
  72. package/src/client/voice/VoiceUDPClient.js +127 -0
  73. package/src/client/voice/VoiceWebSocket.js +246 -0
  74. package/src/client/voice/dispatcher/StreamDispatcher.js +331 -0
  75. package/src/client/voice/opus/BaseOpusEngine.js +60 -0
  76. package/src/client/voice/opus/NodeOpusEngine.js +40 -0
  77. package/src/client/voice/opus/OpusEngineList.js +28 -0
  78. package/src/client/voice/opus/OpusScriptEngine.js +45 -0
  79. package/src/client/voice/player/AudioPlayer.js +170 -0
  80. package/src/client/voice/receiver/VoiceReadable.js +17 -0
  81. package/src/client/voice/receiver/VoiceReceiver.js +219 -0
  82. package/src/client/voice/util/SecretKey.js +16 -0
  83. package/src/client/voice/util/Secretbox.js +33 -0
  84. package/src/client/voice/util/VolumeInterface.js +86 -0
  85. package/src/client/websocket/WebSocketConnection.js +506 -0
  86. package/src/client/websocket/WebSocketManager.js +90 -0
  87. package/src/client/websocket/packets/WebSocketPacketManager.js +110 -0
  88. package/src/client/websocket/packets/handlers/AbstractHandler.js +11 -0
  89. package/src/client/websocket/packets/handlers/ChannelCreate.js +17 -0
  90. package/src/client/websocket/packets/handlers/ChannelDelete.js +20 -0
  91. package/src/client/websocket/packets/handlers/ChannelPinsUpdate.js +37 -0
  92. package/src/client/websocket/packets/handlers/ChannelUpdate.js +11 -0
  93. package/src/client/websocket/packets/handlers/GuildBanAdd.js +23 -0
  94. package/src/client/websocket/packets/handlers/GuildBanRemove.js +20 -0
  95. package/src/client/websocket/packets/handlers/GuildCreate.js +22 -0
  96. package/src/client/websocket/packets/handlers/GuildDelete.js +19 -0
  97. package/src/client/websocket/packets/handlers/GuildEmojisUpdate.js +11 -0
  98. package/src/client/websocket/packets/handlers/GuildIntegrationsUpdate.js +19 -0
  99. package/src/client/websocket/packets/handlers/GuildMemberAdd.js +17 -0
  100. package/src/client/websocket/packets/handlers/GuildMemberRemove.js +13 -0
  101. package/src/client/websocket/packets/handlers/GuildMemberUpdate.js +18 -0
  102. package/src/client/websocket/packets/handlers/GuildMembersChunk.js +33 -0
  103. package/src/client/websocket/packets/handlers/GuildRoleCreate.js +11 -0
  104. package/src/client/websocket/packets/handlers/GuildRoleDelete.js +11 -0
  105. package/src/client/websocket/packets/handlers/GuildRoleUpdate.js +11 -0
  106. package/src/client/websocket/packets/handlers/GuildSync.js +11 -0
  107. package/src/client/websocket/packets/handlers/GuildUpdate.js +11 -0
  108. package/src/client/websocket/packets/handlers/MessageCreate.js +19 -0
  109. package/src/client/websocket/packets/handlers/MessageDelete.js +19 -0
  110. package/src/client/websocket/packets/handlers/MessageDeleteBulk.js +17 -0
  111. package/src/client/websocket/packets/handlers/MessageReactionAdd.js +11 -0
  112. package/src/client/websocket/packets/handlers/MessageReactionRemove.js +11 -0
  113. package/src/client/websocket/packets/handlers/MessageReactionRemoveAll.js +11 -0
  114. package/src/client/websocket/packets/handlers/MessageUpdate.js +11 -0
  115. package/src/client/websocket/packets/handlers/PresenceUpdate.js +76 -0
  116. package/src/client/websocket/packets/handlers/Ready.js +83 -0
  117. package/src/client/websocket/packets/handlers/RelationshipAdd.js +19 -0
  118. package/src/client/websocket/packets/handlers/RelationshipRemove.js +19 -0
  119. package/src/client/websocket/packets/handlers/Resumed.js +28 -0
  120. package/src/client/websocket/packets/handlers/TypingStart.js +68 -0
  121. package/src/client/websocket/packets/handlers/UserGuildSettingsUpdate.js +21 -0
  122. package/src/client/websocket/packets/handlers/UserNoteUpdate.js +12 -0
  123. package/src/client/websocket/packets/handlers/UserSettingsUpdate.js +18 -0
  124. package/src/client/websocket/packets/handlers/UserUpdate.js +11 -0
  125. package/src/client/websocket/packets/handlers/VoiceServerUpdate.js +19 -0
  126. package/src/client/websocket/packets/handlers/VoiceStateUpdate.js +52 -0
  127. package/src/client/websocket/packets/handlers/WebhooksUpdate.js +19 -0
  128. package/src/index.js +66 -0
  129. package/src/sharding/Shard.js +282 -0
  130. package/src/sharding/ShardClientUtil.js +146 -0
  131. package/src/sharding/ShardingManager.js +220 -0
  132. package/src/structures/Attachment.js +75 -0
  133. package/src/structures/CategoryChannel.js +22 -0
  134. package/src/structures/Channel.js +78 -0
  135. package/src/structures/ClientUser.js +447 -0
  136. package/src/structures/ClientUserChannelOverride.js +30 -0
  137. package/src/structures/ClientUserGuildSettings.js +60 -0
  138. package/src/structures/ClientUserSettings.js +80 -0
  139. package/src/structures/DMChannel.js +76 -0
  140. package/src/structures/Emoji.js +256 -0
  141. package/src/structures/GroupDMChannel.js +246 -0
  142. package/src/structures/Guild.js +1461 -0
  143. package/src/structures/GuildAuditLogs.js +371 -0
  144. package/src/structures/GuildChannel.js +537 -0
  145. package/src/structures/GuildMember.js +613 -0
  146. package/src/structures/Invite.js +164 -0
  147. package/src/structures/Message.js +605 -0
  148. package/src/structures/MessageAttachment.js +68 -0
  149. package/src/structures/MessageCollector.js +100 -0
  150. package/src/structures/MessageEmbed.js +386 -0
  151. package/src/structures/MessageMentions.js +144 -0
  152. package/src/structures/MessageReaction.js +96 -0
  153. package/src/structures/NewsChannel.js +24 -0
  154. package/src/structures/OAuth2Application.js +148 -0
  155. package/src/structures/PartialGuild.js +51 -0
  156. package/src/structures/PartialGuildChannel.js +44 -0
  157. package/src/structures/PermissionOverwrites.js +69 -0
  158. package/src/structures/Presence.js +241 -0
  159. package/src/structures/ReactionCollector.js +85 -0
  160. package/src/structures/ReactionEmoji.js +49 -0
  161. package/src/structures/RichEmbed.js +295 -0
  162. package/src/structures/Role.js +376 -0
  163. package/src/structures/StoreChannel.js +25 -0
  164. package/src/structures/TextChannel.js +154 -0
  165. package/src/structures/User.js +329 -0
  166. package/src/structures/UserConnection.js +48 -0
  167. package/src/structures/UserProfile.js +62 -0
  168. package/src/structures/VoiceChannel.js +146 -0
  169. package/src/structures/VoiceRegion.js +50 -0
  170. package/src/structures/Webhook.js +304 -0
  171. package/src/structures/interfaces/Collector.js +179 -0
  172. package/src/structures/interfaces/TextBasedChannel.js +635 -0
  173. package/src/structures/shared/resolvePermissions.js +26 -0
  174. package/src/util/Collection.js +532 -0
  175. package/src/util/Constants.js +845 -0
  176. package/src/util/Permissions.js +306 -0
  177. package/src/util/Snowflake.js +82 -0
  178. package/src/util/Util.js +221 -0
  179. package/test/random.js +207 -0
  180. package/test/shard.js +31 -0
  181. package/test/sharder.js +7 -0
  182. package/test/voice.js +78 -0
  183. package/test/webpack.html +31 -0
  184. package/tsconfig.json +13 -0
  185. package/tslint.json +62 -0
  186. package/typings/discord.js-test.ts +69 -0
  187. package/typings/index.d.ts +2190 -0
  188. package/webpack.config.js +62 -0
@@ -0,0 +1,246 @@
1
+ const Constants = require('../../util/Constants');
2
+ const SecretKey = require('./util/SecretKey');
3
+ const EventEmitter = require('events').EventEmitter;
4
+
5
+ let WebSocket;
6
+ try {
7
+ WebSocket = require('@discordjs/uws');
8
+ } catch (err) {
9
+ WebSocket = require('ws');
10
+ }
11
+
12
+ /**
13
+ * Represents a Voice Connection's WebSocket.
14
+ * @extends {EventEmitter}
15
+ * @private
16
+ */
17
+ class VoiceWebSocket extends EventEmitter {
18
+ constructor(voiceConnection) {
19
+ super();
20
+
21
+ /**
22
+ * The client of this voice WebSocket
23
+ * @type {Client}
24
+ */
25
+ this.client = voiceConnection.voiceManager.client;
26
+
27
+ /**
28
+ * The Voice Connection that this WebSocket serves
29
+ * @type {VoiceConnection}
30
+ */
31
+ this.voiceConnection = voiceConnection;
32
+
33
+ /**
34
+ * How many connection attempts have been made
35
+ * @type {number}
36
+ */
37
+ this.attempts = 0;
38
+
39
+ this.connect();
40
+ this.dead = false;
41
+ this.voiceConnection.on('closing', this.shutdown.bind(this));
42
+ }
43
+
44
+ shutdown() {
45
+ this.dead = true;
46
+ this.reset();
47
+ }
48
+
49
+ /**
50
+ * Resets the current WebSocket.
51
+ */
52
+ reset() {
53
+ if (this.ws) {
54
+ if (this.ws.readyState !== WebSocket.CLOSED) this.ws.close();
55
+ this.ws = null;
56
+ }
57
+ this.clearHeartbeat();
58
+ }
59
+
60
+ /**
61
+ * Starts connecting to the Voice WebSocket Server.
62
+ */
63
+ connect() {
64
+ if (this.dead) return;
65
+ if (this.ws) this.reset();
66
+ if (this.attempts >= 5) {
67
+ this.emit('debug', new Error(`Too many connection attempts (${this.attempts}).`));
68
+ return;
69
+ }
70
+
71
+ this.attempts++;
72
+
73
+ /**
74
+ * The actual WebSocket used to connect to the Voice WebSocket Server.
75
+ * @type {WebSocket}
76
+ */
77
+ this.ws = new WebSocket(`wss://${this.voiceConnection.authentication.endpoint}`);
78
+ this.ws.onopen = this.onOpen.bind(this);
79
+ this.ws.onmessage = this.onMessage.bind(this);
80
+ this.ws.onclose = this.onClose.bind(this);
81
+ this.ws.onerror = this.onError.bind(this);
82
+ }
83
+
84
+ /**
85
+ * Sends data to the WebSocket if it is open.
86
+ * @param {string} data The data to send to the WebSocket
87
+ * @returns {Promise<string>}
88
+ */
89
+ send(data) {
90
+ return new Promise((resolve, reject) => {
91
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
92
+ throw new Error(`Voice websocket not open to send ${data}.`);
93
+ }
94
+ this.ws.send(data, null, error => {
95
+ if (error) reject(error); else resolve(data);
96
+ });
97
+ });
98
+ }
99
+
100
+ /**
101
+ * JSON.stringify's a packet and then sends it to the WebSocket Server.
102
+ * @param {Object} packet The packet to send
103
+ * @returns {Promise<string>}
104
+ */
105
+ sendPacket(packet) {
106
+ try {
107
+ packet = JSON.stringify(packet);
108
+ } catch (error) {
109
+ return Promise.reject(error);
110
+ }
111
+ return this.send(packet);
112
+ }
113
+
114
+ /**
115
+ * Called whenever the WebSocket opens.
116
+ */
117
+ onOpen() {
118
+ this.sendPacket({
119
+ op: Constants.OPCodes.DISPATCH,
120
+ d: {
121
+ server_id: this.voiceConnection.channel.guild.id,
122
+ user_id: this.client.user.id,
123
+ token: this.voiceConnection.authentication.token,
124
+ session_id: this.voiceConnection.authentication.sessionID,
125
+ },
126
+ }).catch(() => {
127
+ this.emit('error', new Error('Tried to send join packet, but the WebSocket is not open.'));
128
+ });
129
+ }
130
+
131
+ /**
132
+ * Called whenever a message is received from the WebSocket.
133
+ * @param {MessageEvent} event The message event that was received
134
+ * @returns {void}
135
+ */
136
+ onMessage(event) {
137
+ try {
138
+ return this.onPacket(JSON.parse(event.data));
139
+ } catch (error) {
140
+ return this.onError(error);
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Called whenever the connection to the WebSocket server is lost.
146
+ */
147
+ onClose() {
148
+ if (!this.dead) this.client.setTimeout(this.connect.bind(this), this.attempts * 1000);
149
+ }
150
+
151
+ /**
152
+ * Called whenever an error occurs with the WebSocket.
153
+ * @param {Error} error The error that occurred
154
+ */
155
+ onError(error) {
156
+ this.emit('error', error);
157
+ }
158
+
159
+ /**
160
+ * Called whenever a valid packet is received from the WebSocket.
161
+ * @param {Object} packet The received packet
162
+ */
163
+ onPacket(packet) {
164
+ switch (packet.op) {
165
+ case Constants.VoiceOPCodes.READY:
166
+ this.setHeartbeat(packet.d.heartbeat_interval);
167
+ /**
168
+ * Emitted once the voice WebSocket receives the ready packet.
169
+ * @param {Object} packet The received packet
170
+ * @event VoiceWebSocket#ready
171
+ */
172
+ this.emit('ready', packet.d);
173
+ break;
174
+ case Constants.VoiceOPCodes.SESSION_DESCRIPTION:
175
+ /**
176
+ * Emitted once the Voice Websocket receives a description of this voice session.
177
+ * @param {string} encryptionMode The type of encryption being used
178
+ * @param {SecretKey} secretKey The secret key used for encryption
179
+ * @event VoiceWebSocket#sessionDescription
180
+ */
181
+ this.emit('sessionDescription', packet.d.mode, new SecretKey(packet.d.secret_key));
182
+ break;
183
+ case Constants.VoiceOPCodes.SPEAKING:
184
+ /**
185
+ * Emitted whenever a speaking packet is received.
186
+ * @param {Object} data
187
+ * @event VoiceWebSocket#speaking
188
+ */
189
+ this.emit('speaking', packet.d);
190
+ break;
191
+ default:
192
+ /**
193
+ * Emitted when an unhandled packet is received.
194
+ * @param {Object} packet
195
+ * @event VoiceWebSocket#unknownPacket
196
+ */
197
+ this.emit('unknownPacket', packet);
198
+ break;
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Sets an interval at which to send a heartbeat packet to the WebSocket.
204
+ * @param {number} interval The interval at which to send a heartbeat packet
205
+ */
206
+ setHeartbeat(interval) {
207
+ if (!interval || isNaN(interval)) {
208
+ this.onError(new Error('Tried to set voice heartbeat but no valid interval was specified.'));
209
+ return;
210
+ }
211
+ if (this.heartbeatInterval) {
212
+ /**
213
+ * Emitted whenver the voice WebSocket encounters a non-fatal error.
214
+ * @param {string} warn The warning
215
+ * @event VoiceWebSocket#warn
216
+ */
217
+ this.emit('warn', 'A voice heartbeat interval is being overwritten');
218
+ clearInterval(this.heartbeatInterval);
219
+ }
220
+ this.heartbeatInterval = this.client.setInterval(this.sendHeartbeat.bind(this), interval);
221
+ }
222
+
223
+ /**
224
+ * Clears a heartbeat interval, if one exists.
225
+ */
226
+ clearHeartbeat() {
227
+ if (!this.heartbeatInterval) {
228
+ this.emit('warn', 'Tried to clear a heartbeat interval that does not exist');
229
+ return;
230
+ }
231
+ clearInterval(this.heartbeatInterval);
232
+ this.heartbeatInterval = null;
233
+ }
234
+
235
+ /**
236
+ * Sends a heartbeat packet.
237
+ */
238
+ sendHeartbeat() {
239
+ this.sendPacket({ op: Constants.VoiceOPCodes.HEARTBEAT, d: null }).catch(() => {
240
+ this.emit('warn', 'Tried to send heartbeat, but connection is not open');
241
+ this.clearHeartbeat();
242
+ });
243
+ }
244
+ }
245
+
246
+ module.exports = VoiceWebSocket;
@@ -0,0 +1,331 @@
1
+ const VolumeInterface = require('../util/VolumeInterface');
2
+ const VoiceBroadcast = require('../VoiceBroadcast');
3
+ const Constants = require('../../../util/Constants');
4
+
5
+ const secretbox = require('../util/Secretbox');
6
+
7
+ const nonce = Buffer.alloc(24);
8
+ nonce.fill(0);
9
+
10
+ /**
11
+ * The class that sends voice packet data to the voice connection.
12
+ * ```js
13
+ * // Obtained using:
14
+ * voiceChannel.join().then(connection => {
15
+ * // You can play a file or a stream here:
16
+ * const dispatcher = connection.playFile('./file.mp3');
17
+ * });
18
+ * ```
19
+ * @implements {VolumeInterface}
20
+ */
21
+ class StreamDispatcher extends VolumeInterface {
22
+ constructor(player, stream, streamOptions) {
23
+ super(streamOptions);
24
+ /**
25
+ * The Audio Player that controls this dispatcher
26
+ * @type {AudioPlayer}
27
+ */
28
+ this.player = player;
29
+ /**
30
+ * The stream that the dispatcher plays
31
+ * @type {ReadableStream|VoiceBroadcast}
32
+ */
33
+ this.stream = stream;
34
+ if (!(this.stream instanceof VoiceBroadcast)) this.startStreaming();
35
+ this.streamOptions = streamOptions;
36
+
37
+ const data = this.streamingData;
38
+ data.length = 20;
39
+ data.missed = 0;
40
+
41
+ /**
42
+ * Whether playing is paused
43
+ * @type {boolean}
44
+ */
45
+ this.paused = false;
46
+ /**
47
+ * Whether this dispatcher has been destroyed
48
+ * @type {boolean}
49
+ */
50
+ this.destroyed = false;
51
+
52
+ this._opus = streamOptions.opus;
53
+ }
54
+
55
+ /**
56
+ * How many passes the dispatcher should take when sending packets to reduce packet loss. Values over 5
57
+ * aren't recommended, as it means you are using 5x more bandwidth. You _can_ edit this at runtime
58
+ * @type {number}
59
+ * @readonly
60
+ */
61
+ get passes() {
62
+ return this.streamOptions.passes || 1;
63
+ }
64
+
65
+ set passes(n) {
66
+ this.streamOptions.passes = n;
67
+ }
68
+
69
+ get streamingData() {
70
+ return this.player.streamingData;
71
+ }
72
+
73
+ /**
74
+ * How long the stream dispatcher has been "speaking" for
75
+ * @type {number}
76
+ * @readonly
77
+ */
78
+ get time() {
79
+ return this.streamingData.count * (this.streamingData.length || 0);
80
+ }
81
+
82
+ /**
83
+ * The total time, taking into account pauses and skips, that the dispatcher has been streaming for
84
+ * @type {number}
85
+ * @readonly
86
+ */
87
+ get totalStreamTime() {
88
+ return this.time + this.streamingData.pausedTime;
89
+ }
90
+
91
+ /**
92
+ * Stops sending voice packets to the voice connection (stream may still progress however).
93
+ */
94
+ pause() { this.setPaused(true); }
95
+
96
+ /**
97
+ * Resumes sending voice packets to the voice connection (may be further on in the stream than when paused).
98
+ */
99
+ resume() { this.setPaused(false); }
100
+
101
+
102
+ /**
103
+ * Stops the current stream permanently and emits an `end` event.
104
+ * @param {string} [reason='user'] An optional reason for stopping the dispatcher
105
+ */
106
+ end(reason = 'user') {
107
+ this.destroy('end', reason);
108
+ }
109
+
110
+ setSpeaking(value) {
111
+ if (this.speaking === value) return;
112
+ if (this.player.voiceConnection.status !== Constants.VoiceStatus.CONNECTED) return;
113
+ this.speaking = value;
114
+ /**
115
+ * Emitted when the dispatcher starts/stops speaking.
116
+ * @event StreamDispatcher#speaking
117
+ * @param {boolean} value Whether or not the dispatcher is speaking
118
+ */
119
+ this.emit('speaking', value);
120
+ }
121
+
122
+
123
+ /**
124
+ * Set the bitrate of the current Opus encoder.
125
+ * @param {number} bitrate New bitrate, in kbps
126
+ * If set to 'auto', the voice channel's bitrate will be used
127
+ */
128
+ setBitrate(bitrate) {
129
+ this.player.setBitrate(bitrate);
130
+ }
131
+
132
+ sendBuffer(buffer, sequence, timestamp, opusPacket) {
133
+ opusPacket = opusPacket || this.player.opusEncoder.encode(buffer);
134
+ const packet = this.createPacket(sequence, timestamp, opusPacket);
135
+ this.sendPacket(packet);
136
+ }
137
+
138
+ sendPacket(packet) {
139
+ let repeats = this.passes;
140
+ /**
141
+ * Emitted whenever the dispatcher has debug information.
142
+ * @event StreamDispatcher#debug
143
+ * @param {string} info The debug info
144
+ */
145
+ this.setSpeaking(true);
146
+ while (repeats-- && this.player.voiceConnection.sockets.udp) {
147
+ this.player.voiceConnection.sockets.udp.send(packet)
148
+ .catch(e => {
149
+ this.setSpeaking(false);
150
+ this.emit('debug', `Failed to send a packet ${e}`);
151
+ });
152
+ }
153
+ }
154
+
155
+ createPacket(sequence, timestamp, buffer) {
156
+ const packetBuffer = Buffer.alloc(buffer.length + 28);
157
+ packetBuffer.fill(0);
158
+ packetBuffer[0] = 0x80;
159
+ packetBuffer[1] = 0x78;
160
+
161
+ packetBuffer.writeUIntBE(sequence, 2, 2);
162
+ packetBuffer.writeUIntBE(timestamp, 4, 4);
163
+ packetBuffer.writeUIntBE(this.player.voiceConnection.authentication.ssrc, 8, 4);
164
+
165
+ packetBuffer.copy(nonce, 0, 0, 12);
166
+ buffer = secretbox.methods.close(buffer, nonce, this.player.voiceConnection.authentication.secretKey.key);
167
+ for (let i = 0; i < buffer.length; i++) packetBuffer[i + 12] = buffer[i];
168
+
169
+ return packetBuffer;
170
+ }
171
+
172
+ processPacket(packet) {
173
+ try {
174
+ if (this.destroyed || !this.player.voiceConnection.authentication.secretKey) {
175
+ this.setSpeaking(false);
176
+ return;
177
+ }
178
+
179
+ const data = this.streamingData;
180
+
181
+ if (this.paused) {
182
+ this.setSpeaking(false);
183
+ data.pausedTime = data.length * 10;
184
+ return;
185
+ }
186
+
187
+ if (!packet) {
188
+ data.missed++;
189
+ data.pausedTime += data.length * 10;
190
+ return;
191
+ }
192
+
193
+ this.started();
194
+ this.missed = 0;
195
+
196
+ this.stepStreamingData();
197
+ this.sendBuffer(null, data.sequence, data.timestamp, packet);
198
+ } catch (e) {
199
+ this.destroy('error', e);
200
+ }
201
+ }
202
+
203
+ process() {
204
+ try {
205
+ if (this.destroyed) {
206
+ this.setSpeaking(false);
207
+ return;
208
+ }
209
+
210
+ const data = this.streamingData;
211
+
212
+ if (data.missed >= 5) {
213
+ this.destroy('end', 'Stream is not generating quickly enough.');
214
+ return;
215
+ }
216
+
217
+ if (this.paused) {
218
+ this.setSpeaking(false);
219
+ // Old code?
220
+ // data.timestamp = data.timestamp + 4294967295 ? data.timestamp + 960 : 0;
221
+ data.pausedTime += data.length * 10;
222
+ this.player.voiceConnection.voiceManager.client.setTimeout(() => this.process(), data.length * 10);
223
+ return;
224
+ }
225
+
226
+ this.started();
227
+
228
+ const buffer = this.readStreamBuffer();
229
+ if (!buffer) {
230
+ data.missed++;
231
+ data.pausedTime += data.length * 10;
232
+ this.player.voiceConnection.voiceManager.client.setTimeout(() => this.process(), data.length * 10);
233
+ return;
234
+ }
235
+
236
+ data.missed = 0;
237
+
238
+ this.stepStreamingData();
239
+
240
+ if (this._opus) {
241
+ this.sendBuffer(null, data.sequence, data.timestamp, buffer);
242
+ } else {
243
+ this.sendBuffer(buffer, data.sequence, data.timestamp);
244
+ }
245
+
246
+ const nextTime = data.length + (data.startTime + data.pausedTime + (data.count * data.length) - Date.now());
247
+ this.player.voiceConnection.voiceManager.client.setTimeout(() => this.process(), nextTime);
248
+ } catch (e) {
249
+ this.destroy('error', e);
250
+ }
251
+ }
252
+
253
+ readStreamBuffer() {
254
+ const data = this.streamingData;
255
+ const bufferLength = (this._opus ? 80 : 1920) * data.channels;
256
+ let buffer = this.stream.read(bufferLength);
257
+ if (this._opus) return buffer;
258
+ if (!buffer) return null;
259
+
260
+ if (buffer.length !== bufferLength) {
261
+ const newBuffer = Buffer.alloc(bufferLength).fill(0);
262
+ buffer.copy(newBuffer);
263
+ buffer = newBuffer;
264
+ }
265
+
266
+ buffer = this.applyVolume(buffer);
267
+ return buffer;
268
+ }
269
+
270
+ started() {
271
+ const data = this.streamingData;
272
+
273
+ if (!data.startTime) {
274
+ /**
275
+ * Emitted once the dispatcher starts streaming.
276
+ * @event StreamDispatcher#start
277
+ */
278
+ this.emit('start');
279
+ data.startTime = Date.now();
280
+ }
281
+ }
282
+
283
+ stepStreamingData() {
284
+ const data = this.streamingData;
285
+ data.count++;
286
+ data.sequence = data.sequence < 65535 ? data.sequence + 1 : 0;
287
+ data.timestamp = (data.timestamp + 960) < 4294967295 ? data.timestamp + 960 : 0;
288
+ }
289
+
290
+ destroy(type, reason) {
291
+ if (this.destroyed) return;
292
+ this.destroyed = true;
293
+ this.setSpeaking(false);
294
+ this.emit(type, reason);
295
+ /**
296
+ * Emitted once the dispatcher ends.
297
+ * @param {string} [reason] The reason the dispatcher ended
298
+ * @event StreamDispatcher#end
299
+ */
300
+ if (type !== 'end') this.emit('end', `destroyed due to ${type} - ${reason}`);
301
+ }
302
+
303
+ startStreaming() {
304
+ if (!this.stream) {
305
+ /**
306
+ * Emitted if the dispatcher encounters an error.
307
+ * @event StreamDispatcher#error
308
+ * @param {string} error The error message
309
+ */
310
+ this.emit('error', 'No stream');
311
+ return;
312
+ }
313
+
314
+ this.stream.on('end', err => this.destroy('end', err || 'stream'));
315
+ this.stream.on('error', err => this.destroy('error', err));
316
+
317
+ const data = this.streamingData;
318
+ data.length = 20;
319
+ data.missed = 0;
320
+
321
+ this.stream.once('readable', () => {
322
+ data.startTime = null;
323
+ data.count = 0;
324
+ this.process();
325
+ });
326
+ }
327
+
328
+ setPaused(paused) { this.setSpeaking(!(this.paused = paused)); }
329
+ }
330
+
331
+ module.exports = StreamDispatcher;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The base opus encoding engine.
3
+ * @private
4
+ */
5
+ class BaseOpus {
6
+ /**
7
+ * @param {Object} [options] The options to apply to the Opus engine
8
+ * @param {number} [options.bitrate=48] The desired bitrate (kbps)
9
+ * @param {boolean} [options.fec=false] Whether to enable forward error correction
10
+ * @param {number} [options.plp=0] The expected packet loss percentage
11
+ */
12
+ constructor({ bitrate = 48, fec = false, plp = 0 } = {}) {
13
+ this.ctl = {
14
+ BITRATE: 4002,
15
+ FEC: 4012,
16
+ PLP: 4014,
17
+ };
18
+
19
+ this.samplingRate = 48000;
20
+ this.channels = 2;
21
+
22
+ /**
23
+ * The desired bitrate (kbps)
24
+ * @type {number}
25
+ */
26
+ this.bitrate = bitrate;
27
+
28
+ /**
29
+ * Miscellaneous Opus options
30
+ * @type {Object}
31
+ */
32
+ this.options = { fec, plp };
33
+ }
34
+
35
+ init() {
36
+ try {
37
+ this.setBitrate(this.bitrate);
38
+
39
+ // Set FEC (forward error correction)
40
+ if (this.options.fec) this.setFEC(this.options.fec);
41
+
42
+ // Set PLP (expected packet loss percentage)
43
+ if (this.options.plp) this.setPLP(this.options.plp);
44
+ } catch (err) {
45
+ // Opus engine likely has no support for libopus CTL
46
+ }
47
+ }
48
+
49
+ encode(buffer) {
50
+ return buffer;
51
+ }
52
+
53
+ decode(buffer) {
54
+ return buffer;
55
+ }
56
+
57
+ destroy() {} // eslint-disable-line no-empty-function
58
+ }
59
+
60
+ module.exports = BaseOpus;
@@ -0,0 +1,40 @@
1
+ const OpusEngine = require('./BaseOpusEngine');
2
+
3
+ let opus;
4
+
5
+ class NodeOpusEngine extends OpusEngine {
6
+ constructor(player) {
7
+ super(player);
8
+ try {
9
+ opus = require('node-opus');
10
+ } catch (err) {
11
+ throw err;
12
+ }
13
+ this.encoder = new opus.OpusEncoder(this.samplingRate, this.channels);
14
+ super.init();
15
+ }
16
+
17
+ setBitrate(bitrate) {
18
+ this.encoder.applyEncoderCTL(this.ctl.BITRATE, Math.min(128, Math.max(16, bitrate)) * 1000);
19
+ }
20
+
21
+ setFEC(enabled) {
22
+ this.encoder.applyEncoderCTL(this.ctl.FEC, enabled ? 1 : 0);
23
+ }
24
+
25
+ setPLP(percent) {
26
+ this.encoder.applyEncoderCTL(this.ctl.PLP, Math.min(100, Math.max(0, percent * 100)));
27
+ }
28
+
29
+ encode(buffer) {
30
+ super.encode(buffer);
31
+ return this.encoder.encode(buffer, 1920);
32
+ }
33
+
34
+ decode(buffer) {
35
+ super.decode(buffer);
36
+ return this.encoder.decode(buffer, 1920);
37
+ }
38
+ }
39
+
40
+ module.exports = NodeOpusEngine;
@@ -0,0 +1,28 @@
1
+ const list = [
2
+ require('./NodeOpusEngine'),
3
+ require('./OpusScriptEngine'),
4
+ ];
5
+
6
+ function fetch(Encoder, engineOptions) {
7
+ try {
8
+ return new Encoder(engineOptions);
9
+ } catch (err) {
10
+ if (err.message.includes('Cannot find module')) return null;
11
+
12
+ // The Opus engine exists, but another error occurred.
13
+ throw err;
14
+ }
15
+ }
16
+
17
+ exports.add = encoder => {
18
+ list.push(encoder);
19
+ };
20
+
21
+ exports.fetch = engineOptions => {
22
+ for (const encoder of list) {
23
+ const fetched = fetch(encoder, engineOptions);
24
+ if (fetched) return fetched;
25
+ }
26
+
27
+ throw new Error('Couldn\'t find an Opus engine.');
28
+ };