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:
sillyfrog
2019-12-06 21:59:57 +10:00
committed by Amish Shah
parent bb8333a4f9
commit 4585d965b4
4 changed files with 36 additions and 12 deletions

View File

@@ -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
## 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.
Without voice support: `npm install discordjs/discord.js`

View File

@@ -422,7 +422,7 @@ class VoiceConnection extends EventEmitter {
udp.on('error', err => this.emit('error', err));
ws.on('ready', this.onReady.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();
}
@@ -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.
* @param {Object} data The received data
* @private
*/
onSpeaking({ user_id, ssrc, speaking }) {
onSpeaking({ user_id, speaking }) {
speaking = new Speaking(speaking).freeze();
const guild = this.channel.guild;
const user = this.client.users.get(user_id);
this.ssrcMap.set(+ssrc, user_id);
const old = this._speaking.get(user_id);
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);

View File

@@ -201,9 +201,9 @@ class VoiceWebSocket extends EventEmitter {
/**
* Emitted whenever a speaking packet is received.
* @param {Object} data
* @event VoiceWebSocket#speaking
* @event VoiceWebSocket#startSpeaking
*/
this.emit('speaking', packet.d);
this.emit('startSpeaking', packet.d);
break;
default:
/**

View File

@@ -3,6 +3,10 @@
const secretbox = require('../util/Secretbox');
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 PacketHandler extends EventEmitter {
@@ -11,6 +15,7 @@ class PacketHandler extends EventEmitter {
this.nonce = Buffer.alloc(24);
this.receiver = receiver;
this.streams = new Map();
this.speakingTimeouts = new Map();
}
get connection() {
@@ -72,13 +77,29 @@ class PacketHandler extends EventEmitter {
return packet;
}
userFromSSRC(ssrc) { return this.connection.client.users.get(this.connection.ssrcMap.get(ssrc)); }
push(buffer) {
const ssrc = buffer.readUInt32BE(8);
const user = this.userFromSSRC(ssrc);
if (!user) return;
let stream = this.streams.get(user.id);
const userStat = this.connection.ssrcMap.get(ssrc);
if (!userStat) return;
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;
stream = stream.stream;
const opusPacket = this.parseBuffer(buffer);