mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-10 00:23:30 +01:00
Merge pull request #50 from hydrabolt/voice-rewrite
Added basic voice capabilities
This commit is contained in:
@@ -532,6 +532,24 @@ var Client = (function (_EventEmitter) {
|
||||
});
|
||||
};
|
||||
|
||||
//def joinVoiceChannel
|
||||
|
||||
Client.prototype.joinVoiceChannel = function joinVoiceChannel(channel) {
|
||||
var callback = arguments.length <= 1 || arguments[1] === undefined ? function (err) {} : arguments[1];
|
||||
|
||||
var self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
self.internal.joinVoiceChannel(channel).then(function (chan) {
|
||||
callback(null, chan);
|
||||
resolve(chan);
|
||||
})["catch"](function (err) {
|
||||
callback(err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_createClass(Client, [{
|
||||
key: "users",
|
||||
get: function get() {
|
||||
|
||||
@@ -22,7 +22,8 @@ var User = require("../Structures/User.js"),
|
||||
Server = require("../Structures/Server.js"),
|
||||
Message = require("../Structures/Message.js"),
|
||||
Role = require("../Structures/Role.js"),
|
||||
Invite = require("../Structures/Invite.js");
|
||||
Invite = require("../Structures/Invite.js"),
|
||||
VoiceConnection = require("../Voice/VoiceConnection.js");
|
||||
|
||||
var zlib;
|
||||
|
||||
@@ -43,9 +44,76 @@ var InternalClient = (function () {
|
||||
this.channels = new Cache();
|
||||
this.servers = new Cache();
|
||||
this.private_channels = new Cache();
|
||||
this.voiceConnection = null;
|
||||
this.resolver = new Resolver(this);
|
||||
}
|
||||
|
||||
//def leaveVoiceChannel
|
||||
|
||||
InternalClient.prototype.leaveVoiceChannel = function leaveVoiceChannel() {
|
||||
var self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (self.voiceConnection) {
|
||||
self.voiceConnection.destroy();
|
||||
self.voiceConnection = null;
|
||||
resolve();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//def joinVoiceChannel
|
||||
|
||||
InternalClient.prototype.joinVoiceChannel = function joinVoiceChannel(chann) {
|
||||
var self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
var channel = self.resolver.resolveVoiceChannel(chann);
|
||||
|
||||
if (channel) {
|
||||
var next = function next() {
|
||||
var session,
|
||||
token,
|
||||
server = channel.server,
|
||||
endpoint;
|
||||
|
||||
var check = function check(m) {
|
||||
var data = JSON.parse(m);
|
||||
if (data.t === "VOICE_STATE_UPDATE") {
|
||||
session = data.d.session_id;
|
||||
} else if (data.t === "VOICE_SERVER_UPDATE") {
|
||||
token = data.d.token;
|
||||
endpoint = data.d.endpoint;
|
||||
var chan = self.voiceConnection = new VoiceConnection(channel, self.client, session, token, server, endpoint);
|
||||
|
||||
chan.on("ready", resolve);
|
||||
chan.on("error", reject);
|
||||
|
||||
self.client.emit("debug", "removed temporary voice websocket listeners");
|
||||
self.websocket.removeListener("message", check);
|
||||
}
|
||||
};
|
||||
|
||||
self.websocket.on("message", check);
|
||||
self.sendWS({
|
||||
op: 4,
|
||||
d: {
|
||||
"guild_id": server.id,
|
||||
"channel_id": channel.id,
|
||||
"self_mute": false,
|
||||
"self_deaf": false
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
self.leaveVoiceChannel().then(next);
|
||||
} else {
|
||||
reject(new Error("voice channel does not exist"));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// def createServer
|
||||
|
||||
InternalClient.prototype.createServer = function createServer(name) {
|
||||
@@ -718,7 +786,6 @@ var InternalClient = (function () {
|
||||
}
|
||||
|
||||
request.put(Endpoints.CHANNEL_PERMISSIONS(channel.id) + "/" + data.id).set("authorization", self.token).send(data).end(function (err) {
|
||||
console.log(err);
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
|
||||
@@ -116,6 +116,14 @@ var Resolver = (function () {
|
||||
return found;
|
||||
};
|
||||
|
||||
Resolver.prototype.resolveVoiceChannel = function resolveVoiceChannel(resource) {
|
||||
// resolveChannel will also work but this is more apt
|
||||
if (resource instanceof VoiceChannel) {
|
||||
return resource;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
Resolver.prototype.resolveChannel = function resolveChannel(resource) {
|
||||
/*
|
||||
accepts a Message, Channel, Server, String ID, User
|
||||
|
||||
98
lib/Voice/AudioEncoder.js
Normal file
98
lib/Voice/AudioEncoder.js
Normal file
@@ -0,0 +1,98 @@
|
||||
"use strict";
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
var cpoc = require("child_process");
|
||||
|
||||
var opus;
|
||||
try {
|
||||
opus = require("node-opus");
|
||||
} catch (e) {
|
||||
// no opus!
|
||||
}
|
||||
var VoicePacket = require("./VoicePacket.js");
|
||||
|
||||
var AudioEncoder = (function () {
|
||||
function AudioEncoder() {
|
||||
_classCallCheck(this, AudioEncoder);
|
||||
|
||||
if (opus) {
|
||||
this.opus = new opus.OpusEncoder(48000, 1);
|
||||
}
|
||||
}
|
||||
|
||||
AudioEncoder.prototype.opusBuffer = function opusBuffer(buffer) {
|
||||
|
||||
return this.opus.encode(buffer, 1920);
|
||||
};
|
||||
|
||||
AudioEncoder.prototype.encodeStream = function encodeStream(stream) {
|
||||
var callback = arguments.length <= 1 || arguments[1] === undefined ? function (err, buffer) {} : arguments[1];
|
||||
|
||||
var self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
var enc = cpoc.spawn("ffmpeg", ["-f", "s16le", "-ar", "48000", "-ac", "1", // this can be 2 but there's no point, discord makes it mono on playback, wasted bandwidth.
|
||||
"-af", "volume=1", "pipe:1", "-i", "-"]);
|
||||
|
||||
stream.pipe(enc.stdin);
|
||||
|
||||
enc.stdout.once("readable", function () {
|
||||
callback(null, {
|
||||
proc: enc,
|
||||
stream: enc.stdout,
|
||||
instream: stream
|
||||
});
|
||||
resolve({
|
||||
proc: enc,
|
||||
stream: enc.stdout,
|
||||
instream: stream
|
||||
});
|
||||
});
|
||||
|
||||
enc.stdout.on("end", function () {
|
||||
callback("end");
|
||||
reject("end");
|
||||
});
|
||||
|
||||
enc.stdout.on("close", function () {
|
||||
callback("close");
|
||||
reject("close");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
AudioEncoder.prototype.encodeFile = function encodeFile(file) {
|
||||
var callback = arguments.length <= 1 || arguments[1] === undefined ? function (err, buffer) {} : arguments[1];
|
||||
|
||||
var self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
var enc = cpoc.spawn("ffmpeg", ["-f", "s16le", "-ar", "48000", "-ac", "1", // this can be 2 but there's no point, discord makes it mono on playback, wasted bandwidth.
|
||||
"-af", "volume=1", "pipe:1", "-i", file]);
|
||||
|
||||
enc.stdout.once("readable", function () {
|
||||
callback(null, {
|
||||
proc: enc,
|
||||
stream: enc.stdout
|
||||
});
|
||||
resolve({
|
||||
proc: enc,
|
||||
stream: enc.stdout
|
||||
});
|
||||
});
|
||||
|
||||
enc.stdout.on("end", function () {
|
||||
callback("end");
|
||||
reject("end");
|
||||
});
|
||||
|
||||
enc.stdout.on("close", function () {
|
||||
callback("close");
|
||||
reject("close");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return AudioEncoder;
|
||||
})();
|
||||
|
||||
module.exports = AudioEncoder;
|
||||
22
lib/Voice/StreamIntent.js
Normal file
22
lib/Voice/StreamIntent.js
Normal file
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
// represents an intent of streaming music
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
||||
|
||||
var EventEmitter = require("events");
|
||||
|
||||
var StreamIntent = (function (_EventEmitter) {
|
||||
_inherits(StreamIntent, _EventEmitter);
|
||||
|
||||
function StreamIntent() {
|
||||
_classCallCheck(this, StreamIntent);
|
||||
|
||||
_EventEmitter.call(this);
|
||||
}
|
||||
|
||||
return StreamIntent;
|
||||
})(EventEmitter);
|
||||
|
||||
module.exports = StreamIntent;
|
||||
323
lib/Voice/VoiceConnection.js
Normal file
323
lib/Voice/VoiceConnection.js
Normal file
@@ -0,0 +1,323 @@
|
||||
"use strict";
|
||||
/*
|
||||
Major credit to izy521 who is the creator of
|
||||
https://github.com/izy521/discord.io,
|
||||
|
||||
without his help voice chat in discord.js would not have
|
||||
been possible!
|
||||
*/
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
||||
|
||||
var WebSocket = require("ws");
|
||||
var dns = require("dns");
|
||||
var udp = require("dgram");
|
||||
var fs = require("fs");
|
||||
var AudioEncoder = require("./AudioEncoder.js");
|
||||
var VoicePacket = require("./VoicePacket.js");
|
||||
var StreamIntent = require("./StreamIntent.js");
|
||||
var EventEmitter = require("events");
|
||||
|
||||
var VoiceConnection = (function (_EventEmitter) {
|
||||
_inherits(VoiceConnection, _EventEmitter);
|
||||
|
||||
function VoiceConnection(channel, client, session, token, server, endpoint) {
|
||||
_classCallCheck(this, VoiceConnection);
|
||||
|
||||
_EventEmitter.call(this);
|
||||
this.id = channel.id;
|
||||
this.voiceChannel = channel;
|
||||
this.client = client;
|
||||
this.session = session;
|
||||
this.token = token;
|
||||
this.server = server;
|
||||
this.endpoint = endpoint.replace(":80", "");
|
||||
this.vWS = null; // vWS means voice websocket
|
||||
this.ready = false;
|
||||
this.vWSData = {};
|
||||
this.encoder = new AudioEncoder();
|
||||
this.udp = null;
|
||||
this.playingIntent = null;
|
||||
this.playing = false;
|
||||
this.streamTime = 0;
|
||||
this.streamProc = null;
|
||||
this.KAI = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
VoiceConnection.prototype.destroy = function destroy() {
|
||||
this.stopPlaying();
|
||||
if (this.KAI) clearInterval(this.KAI);
|
||||
this.vWS.close();
|
||||
this.udp.close();
|
||||
};
|
||||
|
||||
VoiceConnection.prototype.stopPlaying = function stopPlaying() {
|
||||
this.playing = false;
|
||||
this.playingIntent = null;
|
||||
if (this.instream) {
|
||||
this.instream.end();
|
||||
this.instream.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
VoiceConnection.prototype.playStream = function playStream(stream) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var startTime = Date.now();
|
||||
var sequence = 0;
|
||||
var time = 0;
|
||||
var count = 0;
|
||||
|
||||
var length = 20;
|
||||
|
||||
if (self.playingIntent) {
|
||||
self.stopPlaying();
|
||||
}
|
||||
self.playing = true;
|
||||
var retStream = new StreamIntent();
|
||||
var onWarning = false;
|
||||
self.playingIntent = retStream;
|
||||
|
||||
function send() {
|
||||
|
||||
if (!self.playingIntent || !self.playing) {
|
||||
self.setSpeaking(false);
|
||||
retStream.emit("end");
|
||||
self;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var buffer = stream.read(1920);
|
||||
if (!buffer) {
|
||||
setTimeout(send, length * 10); // give chance for some data in 200ms to appear
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer.length !== 1920) {
|
||||
if (onWarning) {
|
||||
retStream.emit("end");
|
||||
stream.destroy();
|
||||
self.setSpeaking(false);
|
||||
return;
|
||||
} else {
|
||||
onWarning = true;
|
||||
setTimeout(send, length * 10); // give chance for some data in 200ms to appear
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
count++;
|
||||
sequence + 10 < 65535 ? sequence += 1 : sequence = 0;
|
||||
time + 9600 < 4294967295 ? time += 960 : time = 0;
|
||||
|
||||
self.sendBuffer(buffer, sequence, time, function (e) {});
|
||||
|
||||
var nextTime = startTime + count * length;
|
||||
|
||||
self.streamTime = count * length;
|
||||
|
||||
setTimeout(send, length + (nextTime - Date.now()));
|
||||
if (!self.playing) self.setSpeaking(true);
|
||||
|
||||
retStream.emit("time", self.streamTime);
|
||||
} catch (e) {
|
||||
retStream.emit("error", e);
|
||||
}
|
||||
}
|
||||
self.setSpeaking(true);
|
||||
send();
|
||||
|
||||
return retStream;
|
||||
};
|
||||
|
||||
VoiceConnection.prototype.setSpeaking = function setSpeaking(value) {
|
||||
this.playing = value;
|
||||
if (this.vWS.readyState === WebSocket.OPEN) this.vWS.send(JSON.stringify({
|
||||
op: 5,
|
||||
d: {
|
||||
speaking: value,
|
||||
delay: 0
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
VoiceConnection.prototype.sendPacket = function sendPacket(packet) {
|
||||
var callback = arguments.length <= 1 || arguments[1] === undefined ? function (err) {} : arguments[1];
|
||||
|
||||
var self = this;
|
||||
self.playing = true;
|
||||
try {
|
||||
if (self.vWS.readyState === WebSocket.OPEN) self.udp.send(packet, 0, packet.length, self.vWSData.port, self.endpoint, callback);
|
||||
} catch (e) {
|
||||
self.playing = false;
|
||||
callback(e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
VoiceConnection.prototype.sendBuffer = function sendBuffer(rawbuffer, sequence, timestamp, callback) {
|
||||
var self = this;
|
||||
self.playing = true;
|
||||
try {
|
||||
if (!self.encoder.opus) {
|
||||
self.playing = false;
|
||||
self.emit("error", "No Opus!");
|
||||
self.client.emit("debug", "Tried to use node-opus, but opus not available - install it!");
|
||||
return;
|
||||
}
|
||||
var buffer = self.encoder.opusBuffer(rawbuffer);
|
||||
var packet = new VoicePacket(buffer, sequence, timestamp, self.vWSData.ssrc);
|
||||
return self.sendPacket(packet, callback);
|
||||
} catch (e) {
|
||||
self.playing = false;
|
||||
self.emit("error", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
VoiceConnection.prototype.test = function test() {
|
||||
this.playFile("C:/users/amish/desktop/audio.mp3").then(function (stream) {
|
||||
stream.on("time", function (time) {
|
||||
console.log("Time", time);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
VoiceConnection.prototype.playFile = function playFile(stream) {
|
||||
var _this = this;
|
||||
|
||||
var callback = arguments.length <= 1 || arguments[1] === undefined ? function (err, str) {} : arguments[1];
|
||||
|
||||
var self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
_this.encoder.encodeFile(stream)["catch"](error).then(function (data) {
|
||||
self.streamProc = data.proc;
|
||||
var intent = self.playStream(data.stream);
|
||||
resolve(intent);
|
||||
callback(null, intent);
|
||||
});
|
||||
function error() {
|
||||
var e = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
|
||||
|
||||
reject(e);
|
||||
callback(e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
VoiceConnection.prototype.playRawStream = function playRawStream(stream) {
|
||||
var _this2 = this;
|
||||
|
||||
var callback = arguments.length <= 1 || arguments[1] === undefined ? function (err, str) {} : arguments[1];
|
||||
|
||||
var self = this;
|
||||
return new Promise(function (resolve, reject) {
|
||||
_this2.encoder.encodeStream(stream)["catch"](error).then(function (data) {
|
||||
self.streamProc = data.proc;
|
||||
self.instream = data.instream;
|
||||
var intent = self.playStream(data.stream);
|
||||
resolve(intent);
|
||||
callback(null, intent);
|
||||
});
|
||||
function error() {
|
||||
var e = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
|
||||
|
||||
reject(e);
|
||||
callback(e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
VoiceConnection.prototype.init = function init() {
|
||||
var _this3 = this;
|
||||
|
||||
var self = this;
|
||||
dns.lookup(this.endpoint, function (err, address, family) {
|
||||
self.endpoint = address;
|
||||
var vWS = self.vWS = new WebSocket("wss://" + _this3.endpoint, null, { rejectUnauthorized: false });
|
||||
var udpClient = self.udp = udp.createSocket("udp4");
|
||||
|
||||
var firstPacket = true;
|
||||
|
||||
var discordIP = "",
|
||||
discordPort = "";
|
||||
|
||||
udpClient.bind({ exclusive: true });
|
||||
udpClient.on('message', function (msg, rinfo) {
|
||||
var buffArr = JSON.parse(JSON.stringify(msg)).data;
|
||||
if (firstPacket === true) {
|
||||
for (var i = 4; i < buffArr.indexOf(0, i); i++) {
|
||||
discordIP += String.fromCharCode(buffArr[i]);
|
||||
}
|
||||
discordPort = msg.readUIntLE(msg.length - 2, 2).toString(10);
|
||||
|
||||
var wsDiscPayload = {
|
||||
"op": 1,
|
||||
"d": {
|
||||
"protocol": "udp",
|
||||
"data": {
|
||||
"address": discordIP,
|
||||
"port": Number(discordPort),
|
||||
"mode": self.vWSData.modes[0] //Plain
|
||||
}
|
||||
}
|
||||
};
|
||||
vWS.send(JSON.stringify(wsDiscPayload));
|
||||
firstPacket = false;
|
||||
}
|
||||
});
|
||||
|
||||
vWS.on("open", function () {
|
||||
vWS.send(JSON.stringify({
|
||||
op: 0,
|
||||
d: {
|
||||
server_id: self.server.id,
|
||||
user_id: self.client.internal.user.id,
|
||||
session_id: self.session,
|
||||
token: self.token
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
var KAI;
|
||||
|
||||
vWS.on("message", function (msg) {
|
||||
var data = JSON.parse(msg);
|
||||
switch (data.op) {
|
||||
case 2:
|
||||
self.vWSData = data.d;
|
||||
|
||||
KAI = setInterval(function () {
|
||||
if (vWS.readyState === WebSocket.OPEN) vWS.send(JSON.stringify({
|
||||
op: 3,
|
||||
d: null
|
||||
}));
|
||||
}, data.d.heartbeat_interval);
|
||||
self.KAI = KAI;
|
||||
|
||||
var udpPacket = new Buffer(70);
|
||||
udpPacket.writeUIntBE(data.d.ssrc, 0, 4);
|
||||
udpClient.send(udpPacket, 0, udpPacket.length, data.d.port, self.endpoint, function (err) {
|
||||
if (err) self.emit("error", err);
|
||||
});
|
||||
break;
|
||||
case 4:
|
||||
|
||||
self.ready = true;
|
||||
self.mode = data.d.mode;
|
||||
self.emit("ready", self);
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return VoiceConnection;
|
||||
})(EventEmitter);
|
||||
|
||||
module.exports = VoiceConnection;
|
||||
26
lib/Voice/VoicePacket.js
Normal file
26
lib/Voice/VoicePacket.js
Normal file
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
var VoicePacket = function VoicePacket(data, sequence, time, ssrc) {
|
||||
_classCallCheck(this, VoicePacket);
|
||||
|
||||
var audioBuffer = data,
|
||||
returnBuffer = new Buffer(audioBuffer.length + 12);
|
||||
|
||||
returnBuffer.fill(0);
|
||||
returnBuffer[0] = 0x80;
|
||||
returnBuffer[1] = 0x78;
|
||||
|
||||
returnBuffer.writeUIntBE(sequence, 2, 2);
|
||||
returnBuffer.writeUIntBE(time, 4, 4);
|
||||
returnBuffer.writeUIntBE(ssrc, 8, 4);
|
||||
|
||||
for (var i = 0; i < audioBuffer.length; i++) {
|
||||
returnBuffer[i + 12] = audioBuffer[i];
|
||||
}
|
||||
|
||||
return returnBuffer;
|
||||
};
|
||||
|
||||
module.exports = VoicePacket;
|
||||
65
lib/index.js
65
lib/index.js
@@ -1,54 +1,17 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
Client: require("./Client/Client.js")
|
||||
};
|
||||
|
||||
var a = new module.exports.Client();
|
||||
a.on("debug", function (m) {
|
||||
return console.log("[debug]", m);
|
||||
});
|
||||
a.on("warn", function (m) {
|
||||
return console.log("[warn]", m);
|
||||
});
|
||||
var start = Date.now();
|
||||
a.on("message", function (m) {
|
||||
if (m.content === "$$$") {
|
||||
a.internal.setTopic(m.channel, "a channel topic!");
|
||||
}
|
||||
});
|
||||
a.on("userTypingStart", function (user, chan) {
|
||||
console.log(user.username + " typing");
|
||||
});
|
||||
a.on("userTypingStop", function (user, chan) {
|
||||
console.log(user.username + " stopped typing");
|
||||
});
|
||||
a.on("ready", function () {
|
||||
for (var _iterator = a.internal.servers, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
|
||||
var _ref;
|
||||
|
||||
if (_isArray) {
|
||||
if (_i >= _iterator.length) break;
|
||||
_ref = _iterator[_i++];
|
||||
} else {
|
||||
_i = _iterator.next();
|
||||
if (_i.done) break;
|
||||
_ref = _i.value;
|
||||
}
|
||||
|
||||
var server = _ref;
|
||||
|
||||
if (server.name === "craptown") {
|
||||
a.leaveServer(server);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function error(e) {
|
||||
throw e;
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
a.login(process.env["discordEmail"], process.env["discordPass"])["catch"](function (e) {
|
||||
return console.log(e);
|
||||
});
|
||||
Client: require("./Client/Client"),
|
||||
Channel: require("./Structures/Channel"),
|
||||
ChannelPermissions: require("./Structures/ChannelPermissions"),
|
||||
Invite: require("./Structures/Invite"),
|
||||
Message: require("./Structures/Message"),
|
||||
PermissionOverwrite: require("./Structures/PermissionOverwrite"),
|
||||
PMChannel: require("./Structures/PMChannel"),
|
||||
Role: require("./Structures/Role"),
|
||||
Server: require("./Structures/Server"),
|
||||
ServerChannel: require("./Structures/ServerChannel"),
|
||||
TextChannel: require("./Structures/TextChannel"),
|
||||
User: require("./Structures/User"),
|
||||
VoiceChannel: require("./Structures/VoiceChannel")
|
||||
};
|
||||
@@ -34,5 +34,8 @@
|
||||
"grunt-browserify": "^4.0.0",
|
||||
"grunt-contrib-uglify": "^0.9.2",
|
||||
"load-grunt-tasks": "^3.2.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"node-opus": "^0.1.11"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,6 +513,24 @@ class Client extends EventEmitter {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
//def joinVoiceChannel
|
||||
joinVoiceChannel(channel, callback=function(err){}){
|
||||
var self = this;
|
||||
return new Promise((resolve, reject)=>{
|
||||
|
||||
self.internal.joinVoiceChannel(channel)
|
||||
.then(chan => {
|
||||
callback(null, chan);
|
||||
resolve(chan);
|
||||
})
|
||||
.catch(err => {
|
||||
callback(err);
|
||||
reject(err);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Client;
|
||||
@@ -20,7 +20,8 @@ var User = require("../Structures/User.js"),
|
||||
Server = require("../Structures/Server.js"),
|
||||
Message = require("../Structures/Message.js"),
|
||||
Role = require("../Structures/Role.js"),
|
||||
Invite = require("../Structures/Invite.js");
|
||||
Invite = require("../Structures/Invite.js"),
|
||||
VoiceConnection = require("../Voice/VoiceConnection.js");
|
||||
|
||||
var zlib;
|
||||
|
||||
@@ -39,8 +40,74 @@ class InternalClient {
|
||||
this.channels = new Cache();
|
||||
this.servers = new Cache();
|
||||
this.private_channels = new Cache();
|
||||
this.voiceConnection = null;
|
||||
this.resolver = new Resolver(this);
|
||||
}
|
||||
|
||||
//def leaveVoiceChannel
|
||||
leaveVoiceChannel(){
|
||||
var self = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
if(self.voiceConnection){
|
||||
self.voiceConnection.destroy();
|
||||
self.voiceConnection = null;
|
||||
resolve();
|
||||
}else{
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//def joinVoiceChannel
|
||||
joinVoiceChannel(chann){
|
||||
var self = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
var channel = self.resolver.resolveVoiceChannel(chann);
|
||||
|
||||
if(channel){
|
||||
|
||||
self.leaveVoiceChannel().then(next);
|
||||
|
||||
function next(){
|
||||
var session, token, server = channel.server, endpoint;
|
||||
|
||||
var check = (m) => {
|
||||
var data = JSON.parse(m);
|
||||
if(data.t === "VOICE_STATE_UPDATE"){
|
||||
session = data.d.session_id;
|
||||
}else if(data.t === "VOICE_SERVER_UPDATE"){
|
||||
token = data.d.token;
|
||||
endpoint = data.d.endpoint;
|
||||
var chan = self.voiceConnection = new VoiceConnection(channel, self.client, session, token, server, endpoint);
|
||||
|
||||
chan.on("ready", resolve);
|
||||
chan.on("error", reject);
|
||||
|
||||
self.client.emit("debug", "removed temporary voice websocket listeners");
|
||||
self.websocket.removeListener("message", check);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
self.websocket.on("message", check);
|
||||
self.sendWS({
|
||||
op : 4,
|
||||
d : {
|
||||
"guild_id" : server.id,
|
||||
"channel_id" : channel.id,
|
||||
"self_mute" : false,
|
||||
"self_deaf" : false
|
||||
}
|
||||
});
|
||||
}
|
||||
}else{
|
||||
reject(new Error("voice channel does not exist"));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// def createServer
|
||||
createServer(name, region = "london") {
|
||||
var self = this;
|
||||
@@ -766,7 +833,6 @@ class InternalClient {
|
||||
.set("authorization", self.token)
|
||||
.send(data)
|
||||
.end(function (err) {
|
||||
console.log(err);
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
|
||||
@@ -98,6 +98,14 @@ class Resolver {
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
resolveVoiceChannel(resource) {
|
||||
// resolveChannel will also work but this is more apt
|
||||
if(resource instanceof VoiceChannel){
|
||||
return resource;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
resolveChannel(resource) {
|
||||
/*
|
||||
|
||||
101
src/Voice/AudioEncoder.js
Normal file
101
src/Voice/AudioEncoder.js
Normal file
@@ -0,0 +1,101 @@
|
||||
"use strict";
|
||||
|
||||
var cpoc = require("child_process");
|
||||
|
||||
var opus;
|
||||
try{
|
||||
opus = require("node-opus");
|
||||
}catch(e){
|
||||
// no opus!
|
||||
}
|
||||
var VoicePacket = require("./VoicePacket.js");
|
||||
|
||||
class AudioEncoder{
|
||||
constructor(){
|
||||
if(opus){
|
||||
this.opus = new opus.OpusEncoder(48000, 1);
|
||||
}
|
||||
}
|
||||
|
||||
opusBuffer(buffer){
|
||||
|
||||
return this.opus.encode(buffer, 1920);
|
||||
|
||||
}
|
||||
|
||||
encodeStream(stream, callback=function(err, buffer){}){
|
||||
var self = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
var enc = cpoc.spawn("ffmpeg" , [
|
||||
"-f", "s16le",
|
||||
"-ar", "48000",
|
||||
"-ac", "1", // this can be 2 but there's no point, discord makes it mono on playback, wasted bandwidth.
|
||||
"-af", "volume=1",
|
||||
"pipe:1",
|
||||
"-i", "-"
|
||||
]);
|
||||
|
||||
stream.pipe(enc.stdin);
|
||||
|
||||
enc.stdout.once("readable", function() {
|
||||
callback(null, {
|
||||
proc : enc,
|
||||
stream : enc.stdout,
|
||||
instream : stream
|
||||
});
|
||||
resolve({
|
||||
proc : enc,
|
||||
stream : enc.stdout,
|
||||
instream : stream
|
||||
});
|
||||
});
|
||||
|
||||
enc.stdout.on("end", function() {
|
||||
callback("end");
|
||||
reject("end");
|
||||
});
|
||||
|
||||
enc.stdout.on("close", function() {
|
||||
callback("close");
|
||||
reject("close");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
encodeFile(file, callback=function(err, buffer){}){
|
||||
var self = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
var enc = cpoc.spawn("ffmpeg" , [
|
||||
"-f", "s16le",
|
||||
"-ar", "48000",
|
||||
"-ac", "1", // this can be 2 but there's no point, discord makes it mono on playback, wasted bandwidth.
|
||||
"-af", "volume=1",
|
||||
"pipe:1",
|
||||
"-i", file
|
||||
]);
|
||||
|
||||
enc.stdout.once("readable", function() {
|
||||
callback(null, {
|
||||
proc : enc,
|
||||
stream : enc.stdout
|
||||
});
|
||||
resolve({
|
||||
proc : enc,
|
||||
stream : enc.stdout
|
||||
});
|
||||
});
|
||||
|
||||
enc.stdout.on("end", function() {
|
||||
callback("end");
|
||||
reject("end");
|
||||
});
|
||||
|
||||
enc.stdout.on("close", function() {
|
||||
callback("close");
|
||||
reject("close");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AudioEncoder;
|
||||
11
src/Voice/StreamIntent.js
Normal file
11
src/Voice/StreamIntent.js
Normal file
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
// represents an intent of streaming music
|
||||
var EventEmitter = require("events");
|
||||
|
||||
class StreamIntent extends EventEmitter{
|
||||
constructor(){
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StreamIntent;
|
||||
315
src/Voice/VoiceConnection.js
Normal file
315
src/Voice/VoiceConnection.js
Normal file
@@ -0,0 +1,315 @@
|
||||
"use strict";
|
||||
/*
|
||||
Major credit to izy521 who is the creator of
|
||||
https://github.com/izy521/discord.io,
|
||||
|
||||
without his help voice chat in discord.js would not have
|
||||
been possible!
|
||||
*/
|
||||
|
||||
var WebSocket = require("ws");
|
||||
var dns = require("dns");
|
||||
var udp = require("dgram");
|
||||
var fs = require("fs");
|
||||
var AudioEncoder = require("./AudioEncoder.js");
|
||||
var VoicePacket = require("./VoicePacket.js");
|
||||
var StreamIntent = require("./StreamIntent.js");
|
||||
var EventEmitter = require("events");
|
||||
|
||||
class VoiceConnection extends EventEmitter {
|
||||
constructor(channel, client, session, token, server, endpoint) {
|
||||
super();
|
||||
this.id = channel.id;
|
||||
this.voiceChannel = channel;
|
||||
this.client = client;
|
||||
this.session = session;
|
||||
this.token = token;
|
||||
this.server = server;
|
||||
this.endpoint = endpoint.replace(":80", "");
|
||||
this.vWS = null; // vWS means voice websocket
|
||||
this.ready = false;
|
||||
this.vWSData = {};
|
||||
this.encoder = new AudioEncoder();
|
||||
this.udp = null;
|
||||
this.playingIntent = null;
|
||||
this.playing = false;
|
||||
this.streamTime = 0;
|
||||
this.streamProc = null;
|
||||
this.KAI = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.stopPlaying();
|
||||
if(this.KAI)
|
||||
clearInterval(this.KAI);
|
||||
this.vWS.close();
|
||||
this.udp.close();
|
||||
}
|
||||
|
||||
stopPlaying() {
|
||||
this.playing = false;
|
||||
this.playingIntent = null;
|
||||
if(this.instream){
|
||||
this.instream.end();
|
||||
this.instream.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
playStream(stream) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var startTime = Date.now();
|
||||
var sequence = 0;
|
||||
var time = 0;
|
||||
var count = 0;
|
||||
|
||||
var length = 20;
|
||||
|
||||
if (self.playingIntent) {
|
||||
self.stopPlaying();
|
||||
}
|
||||
self.playing = true;
|
||||
var retStream = new StreamIntent();
|
||||
var onWarning = false;
|
||||
self.playingIntent = retStream;
|
||||
|
||||
function send() {
|
||||
|
||||
if (!self.playingIntent || !self.playing) {
|
||||
self.setSpeaking(false);
|
||||
retStream.emit("end");
|
||||
self
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var buffer = stream.read(1920);
|
||||
if (!buffer) {
|
||||
setTimeout(send, length * 10); // give chance for some data in 200ms to appear
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer.length !== 1920) {
|
||||
if (onWarning) {
|
||||
retStream.emit("end");
|
||||
stream.destroy();
|
||||
self.setSpeaking(false);
|
||||
return;
|
||||
} else {
|
||||
onWarning = true;
|
||||
setTimeout(send, length * 10); // give chance for some data in 200ms to appear
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
count++;
|
||||
sequence + 10 < 65535 ? sequence += 1 : sequence = 0;
|
||||
time + 9600 < 4294967295 ? time += 960 : time = 0;
|
||||
|
||||
self.sendBuffer(buffer, sequence, time, (e) => { });
|
||||
|
||||
var nextTime = startTime + (count * length);
|
||||
|
||||
self.streamTime = count * length;
|
||||
|
||||
setTimeout(send, length + (nextTime - Date.now()));
|
||||
if (!self.playing)
|
||||
self.setSpeaking(true);
|
||||
|
||||
retStream.emit("time", self.streamTime);
|
||||
|
||||
|
||||
} catch (e) {
|
||||
retStream.emit("error", e);
|
||||
}
|
||||
}
|
||||
self.setSpeaking(true);
|
||||
send();
|
||||
|
||||
return retStream;
|
||||
}
|
||||
|
||||
setSpeaking(value) {
|
||||
this.playing = value;
|
||||
if (this.vWS.readyState === WebSocket.OPEN)
|
||||
this.vWS.send(JSON.stringify({
|
||||
op: 5,
|
||||
d: {
|
||||
speaking: value,
|
||||
delay: 0
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
sendPacket(packet, callback = function (err) { }) {
|
||||
var self = this;
|
||||
self.playing = true;
|
||||
try {
|
||||
if (self.vWS.readyState === WebSocket.OPEN)
|
||||
self.udp.send(packet, 0, packet.length, self.vWSData.port, self.endpoint, callback);
|
||||
} catch (e) {
|
||||
self.playing = false;
|
||||
callback(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sendBuffer(rawbuffer, sequence, timestamp, callback) {
|
||||
var self = this;
|
||||
self.playing = true;
|
||||
try {
|
||||
if(!self.encoder.opus){
|
||||
self.playing=false;
|
||||
self.emit("error", "No Opus!");
|
||||
self.client.emit("debug", "Tried to use node-opus, but opus not available - install it!");
|
||||
return;
|
||||
}
|
||||
var buffer = self.encoder.opusBuffer(rawbuffer);
|
||||
var packet = new VoicePacket(buffer, sequence, timestamp, self.vWSData.ssrc);
|
||||
return self.sendPacket(packet, callback);
|
||||
|
||||
} catch (e) {
|
||||
self.playing = false;
|
||||
self.emit("error", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
test() {
|
||||
this.playFile("C:/users/amish/desktop/audio.mp3")
|
||||
.then(stream => {
|
||||
stream.on("time", time => {
|
||||
console.log("Time", time);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
playFile(stream, callback = function (err, str) { }) {
|
||||
var self = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
this.encoder
|
||||
.encodeFile(stream)
|
||||
.catch(error)
|
||||
.then(data => {
|
||||
self.streamProc = data.proc;
|
||||
var intent = self.playStream(data.stream);
|
||||
resolve(intent);
|
||||
callback(null, intent);
|
||||
|
||||
});
|
||||
function error(e = true) {
|
||||
reject(e);
|
||||
callback(e);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
playRawStream(stream, callback = function (err, str) { }) {
|
||||
var self = this;
|
||||
return new Promise((resolve, reject) => {
|
||||
this.encoder
|
||||
.encodeStream(stream)
|
||||
.catch(error)
|
||||
.then(data => {
|
||||
self.streamProc = data.proc;
|
||||
self.instream = data.instream;
|
||||
var intent = self.playStream(data.stream);
|
||||
resolve(intent);
|
||||
callback(null, intent);
|
||||
|
||||
});
|
||||
function error(e = true) {
|
||||
reject(e);
|
||||
callback(e);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
init() {
|
||||
var self = this;
|
||||
dns.lookup(this.endpoint, (err, address, family) => {
|
||||
self.endpoint = address;
|
||||
var vWS = self.vWS = new WebSocket("wss://" + this.endpoint, null, { rejectUnauthorized: false });
|
||||
var udpClient = self.udp = udp.createSocket("udp4");
|
||||
|
||||
var firstPacket = true;
|
||||
|
||||
var discordIP = "", discordPort = "";
|
||||
|
||||
udpClient.bind({ exclusive: true });
|
||||
udpClient.on('message', function (msg, rinfo) {
|
||||
var buffArr = JSON.parse(JSON.stringify(msg)).data;
|
||||
if (firstPacket === true) {
|
||||
for (var i = 4; i < buffArr.indexOf(0, i); i++) {
|
||||
discordIP += String.fromCharCode(buffArr[i]);
|
||||
}
|
||||
discordPort = msg.readUIntLE(msg.length - 2, 2).toString(10);
|
||||
|
||||
var wsDiscPayload = {
|
||||
"op": 1,
|
||||
"d": {
|
||||
"protocol": "udp",
|
||||
"data": {
|
||||
"address": discordIP,
|
||||
"port": Number(discordPort),
|
||||
"mode": self.vWSData.modes[0] //Plain
|
||||
}
|
||||
}
|
||||
}
|
||||
vWS.send(JSON.stringify(wsDiscPayload));
|
||||
firstPacket = false;
|
||||
}
|
||||
});
|
||||
|
||||
vWS.on("open", () => {
|
||||
vWS.send(JSON.stringify({
|
||||
op: 0,
|
||||
d: {
|
||||
server_id: self.server.id,
|
||||
user_id: self.client.internal.user.id,
|
||||
session_id: self.session,
|
||||
token: self.token
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
var KAI;
|
||||
|
||||
vWS.on("message", (msg) => {
|
||||
var data = JSON.parse(msg);
|
||||
switch (data.op) {
|
||||
case 2:
|
||||
self.vWSData = data.d;
|
||||
|
||||
KAI = setInterval(() => {
|
||||
if (vWS.readyState === WebSocket.OPEN)
|
||||
vWS.send(JSON.stringify({
|
||||
op: 3,
|
||||
d: null
|
||||
}));
|
||||
}, data.d.heartbeat_interval);
|
||||
self.KAI = KAI;
|
||||
|
||||
var udpPacket = new Buffer(70);
|
||||
udpPacket.writeUIntBE(data.d.ssrc, 0, 4);
|
||||
udpClient.send(udpPacket, 0, udpPacket.length, data.d.port, self.endpoint, err => {
|
||||
if (err)
|
||||
self.emit("error", err)
|
||||
});
|
||||
break;
|
||||
case 4:
|
||||
|
||||
self.ready = true;
|
||||
self.mode = data.d.mode;
|
||||
self.emit("ready", self);
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VoiceConnection;
|
||||
26
src/Voice/VoicePacket.js
Normal file
26
src/Voice/VoicePacket.js
Normal file
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
class VoicePacket{
|
||||
constructor(data, sequence, time, ssrc){
|
||||
|
||||
var audioBuffer = data,
|
||||
returnBuffer = new Buffer(audioBuffer.length + 12);
|
||||
|
||||
returnBuffer.fill(0);
|
||||
returnBuffer[0] = 0x80;
|
||||
returnBuffer[1] = 0x78;
|
||||
|
||||
returnBuffer.writeUIntBE(sequence, 2, 2);
|
||||
returnBuffer.writeUIntBE(time, 4, 4);
|
||||
returnBuffer.writeUIntBE(ssrc, 8, 4);
|
||||
|
||||
for (var i=0; i<audioBuffer.length; i++) {
|
||||
returnBuffer[i + 12] = audioBuffer[i];
|
||||
}
|
||||
|
||||
return returnBuffer;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VoicePacket;
|
||||
47
src/index.js
47
src/index.js
@@ -1,34 +1,15 @@
|
||||
module.exports = {
|
||||
Client : require("./Client/Client.js")
|
||||
}
|
||||
|
||||
var a = new module.exports.Client();
|
||||
a.on("debug", (m) => console.log("[debug]",m));
|
||||
a.on("warn", (m) => console.log("[warn]", m));
|
||||
var start = Date.now();
|
||||
a.on("message", m => {
|
||||
if(m.content === "$$$"){
|
||||
a.internal.setTopic(m.channel, "a channel topic!");
|
||||
}
|
||||
});
|
||||
a.on("userTypingStart", (user, chan) => {
|
||||
console.log(user.username + " typing");
|
||||
});
|
||||
a.on("userTypingStop", (user, chan) => {
|
||||
console.log(user.username + " stopped typing");
|
||||
});
|
||||
a.on("ready", () => {
|
||||
for(var server of a.internal.servers){
|
||||
if(server.name === "craptown"){
|
||||
a.leaveServer(server);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function error(e){
|
||||
throw e;
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
|
||||
a.login(process.env["discordEmail"], process.env["discordPass"]).catch((e)=>console.log(e));
|
||||
Client : require("./Client/Client"),
|
||||
Channel : require("./Structures/Channel"),
|
||||
ChannelPermissions : require("./Structures/ChannelPermissions"),
|
||||
Invite : require("./Structures/Invite"),
|
||||
Message : require("./Structures/Message"),
|
||||
PermissionOverwrite : require("./Structures/PermissionOverwrite"),
|
||||
PMChannel : require("./Structures/PMChannel"),
|
||||
Role : require("./Structures/Role"),
|
||||
Server : require("./Structures/Server"),
|
||||
ServerChannel : require("./Structures/ServerChannel"),
|
||||
TextChannel : require("./Structures/TextChannel"),
|
||||
User : require("./Structures/User"),
|
||||
VoiceChannel : require("./Structures/VoiceChannel"),
|
||||
}
|
||||
112
test/bot.1.js
112
test/bot.1.js
@@ -1,69 +1,63 @@
|
||||
var Discord = require("../");
|
||||
var Member = require("../lib/Member.js");
|
||||
var mybot = new Discord.Client({
|
||||
compress : true,
|
||||
catchup : "all"
|
||||
});
|
||||
var fs = require("fs");
|
||||
var request = require("request").defaults({ encoding: null });
|
||||
var client = new Discord.Client();
|
||||
var request = require("superagent");
|
||||
client.on("debug", (m) => console.log("[debug]", m));
|
||||
client.on("warn", (m) => console.log("[warn]", m));
|
||||
var start = Date.now();
|
||||
|
||||
Discord.patchStrings();
|
||||
|
||||
var server, channel, message, sentMessage = false;
|
||||
|
||||
mybot.on("message", function (message) {
|
||||
|
||||
console.log("Everyone mentioned? " + doned);
|
||||
doned++;
|
||||
if (message.content.substr(0, 3) !== "$$$") {
|
||||
return;
|
||||
}
|
||||
|
||||
// we can go ahead :)
|
||||
|
||||
var user;
|
||||
if (message.mentions.length > 0) {
|
||||
user = message.mentions[0];
|
||||
} else {
|
||||
user = message.sender;
|
||||
}
|
||||
|
||||
mybot.reply(message, "Hello! It has been " + ((Date.now() - message.timestamp) - this.timeoffset) + "ms since you sent that.");
|
||||
});
|
||||
|
||||
var doned = 0;
|
||||
|
||||
mybot.once("ready", function () {
|
||||
console.log("im ready");
|
||||
|
||||
for (var server of mybot.servers) {
|
||||
if (server.name === "test-server") {
|
||||
mybot.leaveServer(server);
|
||||
client.on("message", m => {
|
||||
if (m.content === "&init") {
|
||||
for (var channel of m.channel.server.channels) {
|
||||
if (channel instanceof Discord.VoiceChannel) {
|
||||
client.reply(m, channel.name + " - " + channel.id);
|
||||
client.joinVoiceChannel(channel).catch(error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m.content.startsWith("$$$ stop")) {
|
||||
if (client.internal.voiceConnection) {
|
||||
client.internal.voiceConnection.stopPlaying();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (m.content.startsWith("$$$")) {
|
||||
var chan;
|
||||
var rest = m.content.split(" ");
|
||||
rest.splice(0, 1);
|
||||
rest = rest.join(" ");
|
||||
if (client.internal.voiceConnection) {
|
||||
client.reply(m, "ok, I'll play that for you");
|
||||
var connection = client.internal.voiceConnection;
|
||||
connection.playFile("C:/users/amish/desktop/" + rest);
|
||||
}
|
||||
} if (m.content.startsWith("pipeit")) {
|
||||
var chan;
|
||||
var rest = m.content.split(" ");
|
||||
rest.splice(0, 1);
|
||||
rest = rest.join(" ");
|
||||
|
||||
if (client.internal.voiceConnection) {
|
||||
var connection = client.internal.voiceConnection;
|
||||
|
||||
connection.playFile(rest).then(intent => {
|
||||
client.reply(m, "playing!").then((msg) => {
|
||||
|
||||
intent.on("end", () => {
|
||||
client.updateMessage(msg, "that song has finished now.");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mybot.on("messageUpdate", function(newMessage, oldMessage){
|
||||
mybot.reply(newMessage, JSON.stringify(newMessage.embeds));
|
||||
})
|
||||
|
||||
mybot.on("serverUpdate", function (oldserver, newserver) {
|
||||
console.log("server changed! " + mybot.servers.length);
|
||||
})
|
||||
|
||||
|
||||
mybot.on("channelUpdate", function (oldChan, newChan) {
|
||||
|
||||
});
|
||||
|
||||
|
||||
function dump(msg) {
|
||||
console.log("dump", msg);
|
||||
function error(e) {
|
||||
console.log(e.stack);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function error(err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
mybot.login(process.env["ds_email"], process.env["ds_password"]).catch(error);
|
||||
client.login(process.env["discordEmail"], process.env["discordPass"]).catch((e) => console.log(e));
|
||||
Reference in New Issue
Block a user