mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-17 03:53:29 +01:00
Start/Stop speaking events on UDP packets (#3578)
* Start/Stop speaking using incomming UDP packets * Fix ESLint errors * Updates for styling consistency Co-Authored-By: Gryffon Bellish <owenbellish@gmail.com> * Minor improvements * Acutally use previousTimeout * Use BaseClient setTimeout and refresh() * Update README to match node version for refresh() * Update comment to match startSpeaking * Correctly report Priority bit * Fix ESlint errors
This commit is contained in:
@@ -38,7 +38,7 @@ discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to
|
|||||||
- 100% coverage of the Discord API
|
- 100% coverage of the Discord API
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
**Node.js 10.0.0 or newer is required.**
|
**Node.js 10.2.0 or newer is required.**
|
||||||
Ignore any warnings about unmet peer dependencies, as they're all optional.
|
Ignore any warnings about unmet peer dependencies, as they're all optional.
|
||||||
|
|
||||||
Without voice support: `npm install discordjs/discord.js`
|
Without voice support: `npm install discordjs/discord.js`
|
||||||
|
|||||||
@@ -422,7 +422,7 @@ class VoiceConnection extends EventEmitter {
|
|||||||
udp.on('error', err => this.emit('error', err));
|
udp.on('error', err => this.emit('error', err));
|
||||||
ws.on('ready', this.onReady.bind(this));
|
ws.on('ready', this.onReady.bind(this));
|
||||||
ws.on('sessionDescription', this.onSessionDescription.bind(this));
|
ws.on('sessionDescription', this.onSessionDescription.bind(this));
|
||||||
ws.on('speaking', this.onSpeaking.bind(this));
|
ws.on('startSpeaking', this.onStartSpeaking.bind(this));
|
||||||
|
|
||||||
this.sockets.ws.connect();
|
this.sockets.ws.connect();
|
||||||
}
|
}
|
||||||
@@ -465,16 +465,19 @@ class VoiceConnection extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onStartSpeaking({ user_id, ssrc, speaking }) {
|
||||||
|
this.ssrcMap.set(+ssrc, { userID: user_id, speaking: speaking });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked when a speaking event is received.
|
* Invoked when a speaking event is received.
|
||||||
* @param {Object} data The received data
|
* @param {Object} data The received data
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
onSpeaking({ user_id, ssrc, speaking }) {
|
onSpeaking({ user_id, speaking }) {
|
||||||
speaking = new Speaking(speaking).freeze();
|
speaking = new Speaking(speaking).freeze();
|
||||||
const guild = this.channel.guild;
|
const guild = this.channel.guild;
|
||||||
const user = this.client.users.get(user_id);
|
const user = this.client.users.get(user_id);
|
||||||
this.ssrcMap.set(+ssrc, user_id);
|
|
||||||
const old = this._speaking.get(user_id);
|
const old = this._speaking.get(user_id);
|
||||||
this._speaking.set(user_id, speaking);
|
this._speaking.set(user_id, speaking);
|
||||||
/**
|
/**
|
||||||
@@ -504,7 +507,7 @@ class VoiceConnection extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
play() {} // eslint-disable-line no-empty-function
|
play() { } // eslint-disable-line no-empty-function
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayInterface.applyToClass(VoiceConnection);
|
PlayInterface.applyToClass(VoiceConnection);
|
||||||
|
|||||||
@@ -201,9 +201,9 @@ class VoiceWebSocket extends EventEmitter {
|
|||||||
/**
|
/**
|
||||||
* Emitted whenever a speaking packet is received.
|
* Emitted whenever a speaking packet is received.
|
||||||
* @param {Object} data
|
* @param {Object} data
|
||||||
* @event VoiceWebSocket#speaking
|
* @event VoiceWebSocket#startSpeaking
|
||||||
*/
|
*/
|
||||||
this.emit('speaking', packet.d);
|
this.emit('startSpeaking', packet.d);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
const secretbox = require('../util/Secretbox');
|
const secretbox = require('../util/Secretbox');
|
||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
|
|
||||||
|
// The delay between packets when a user is considered to have stopped speaking
|
||||||
|
// https://github.com/discordjs/discord.js/issues/3524#issuecomment-540373200
|
||||||
|
const DISCORD_SPEAKING_DELAY = 250;
|
||||||
|
|
||||||
class Readable extends require('stream').Readable { _read() {} } // eslint-disable-line no-empty-function
|
class Readable extends require('stream').Readable { _read() {} } // eslint-disable-line no-empty-function
|
||||||
|
|
||||||
class PacketHandler extends EventEmitter {
|
class PacketHandler extends EventEmitter {
|
||||||
@@ -11,6 +15,7 @@ class PacketHandler extends EventEmitter {
|
|||||||
this.nonce = Buffer.alloc(24);
|
this.nonce = Buffer.alloc(24);
|
||||||
this.receiver = receiver;
|
this.receiver = receiver;
|
||||||
this.streams = new Map();
|
this.streams = new Map();
|
||||||
|
this.speakingTimeouts = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
get connection() {
|
get connection() {
|
||||||
@@ -72,13 +77,29 @@ class PacketHandler extends EventEmitter {
|
|||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
userFromSSRC(ssrc) { return this.connection.client.users.get(this.connection.ssrcMap.get(ssrc)); }
|
|
||||||
|
|
||||||
push(buffer) {
|
push(buffer) {
|
||||||
const ssrc = buffer.readUInt32BE(8);
|
const ssrc = buffer.readUInt32BE(8);
|
||||||
const user = this.userFromSSRC(ssrc);
|
const userStat = this.connection.ssrcMap.get(ssrc);
|
||||||
if (!user) return;
|
if (!userStat) return;
|
||||||
let stream = this.streams.get(user.id);
|
|
||||||
|
let speakingTimeout = this.speakingTimeouts.get(ssrc);
|
||||||
|
if (typeof speakingTimeout === 'undefined') {
|
||||||
|
this.connection.onSpeaking({ user_id: userStat.userID, ssrc: ssrc, speaking: userStat.speaking });
|
||||||
|
speakingTimeout = this.receiver.connection.client.setTimeout(() => {
|
||||||
|
try {
|
||||||
|
this.connection.onSpeaking({ user_id: userStat.userID, ssrc: ssrc, speaking: 0 });
|
||||||
|
this.receiver.connection.client.clearTimeout(speakingTimeout);
|
||||||
|
this.speakingTimeouts.delete(ssrc);
|
||||||
|
} catch (ex) {
|
||||||
|
// Connection already closed, ignore
|
||||||
|
}
|
||||||
|
}, DISCORD_SPEAKING_DELAY);
|
||||||
|
this.speakingTimeouts.set(ssrc, speakingTimeout);
|
||||||
|
} else {
|
||||||
|
speakingTimeout.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
let stream = this.streams.get(userStat.userID);
|
||||||
if (!stream) return;
|
if (!stream) return;
|
||||||
stream = stream.stream;
|
stream = stream.stream;
|
||||||
const opusPacket = this.parseBuffer(buffer);
|
const opusPacket = this.parseBuffer(buffer);
|
||||||
|
|||||||
Reference in New Issue
Block a user