diff --git a/.gitignore b/.gitignore index 36326a28..e48f0097 100644 --- a/.gitignore +++ b/.gitignore @@ -36,5 +36,4 @@ build/Release node_modules test/auth.json examples/auth.json -test/msgbot.js docs/_build \ No newline at end of file diff --git a/lib/Client/Client.js b/lib/Client/Client.js index 5c3365da..b108a559 100644 --- a/lib/Client/Client.js +++ b/lib/Client/Client.js @@ -359,6 +359,38 @@ var Client = (function (_EventEmitter) { return this.removeMemberFromRole(member, role, callback); }; + //def addMemberToRole + + Client.prototype.addMemberToRoles = function addMemberToRoles(member, roles) { + var callback = arguments.length <= 2 || arguments[2] === undefined ? function () /*err*/{} : arguments[2]; + + return this.internal.addMemberToRoles(member, roles).then(callback, errCB(callback)); + }; + + // def addUserToRole + + Client.prototype.addUserToRoles = function addUserToRoles(member, roles) { + var callback = arguments.length <= 2 || arguments[2] === undefined ? function () /*err*/{} : arguments[2]; + + return this.addMemberToRoles(member, roles, callback); + }; + + // def removeMemberFromRole + + Client.prototype.removeMemberFromRoles = function removeMemberFromRoles(member, roles) { + var callback = arguments.length <= 2 || arguments[2] === undefined ? function () /*err*/{} : arguments[2]; + + return this.internal.removeMemberFromRoles(member, roles).then(callback, errCB(callback)); + }; + + // def removeUserFromRole + + Client.prototype.removeUserFromRoles = function removeUserFromRoles(member, roles) { + var callback = arguments.length <= 2 || arguments[2] === undefined ? function () /*err*/{} : arguments[2]; + + return this.removeMemberFromRoles(member, roles, callback); + }; + // def createInvite Client.prototype.createInvite = function createInvite(chanServ, options) { diff --git a/lib/Client/InternalClient.js b/lib/Client/InternalClient.js index 938e2b08..5b8b47ad 100644 --- a/lib/Client/InternalClient.js +++ b/lib/Client/InternalClient.js @@ -662,6 +662,48 @@ var InternalClient = (function () { }).end(); }; + //def addMemberToRole + + InternalClient.prototype.addMemberToRoles = function addMemberToRoles(member, roles) { + member = this.resolver.resolveUser(member); + + if (!member) { + return Promise.reject(new Error("member not in server")); + } + + if (!Array.isArray(roles) || roles.length === 0) { + return Promise.reject(new Error("invalid array of roles")); + } + + var roleIDS = roles[0].server.memberMap[member.id].roles.map(function (r) { + return r.id; + }); + + for (var _iterator3 = roles, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { + var _ref3; + + if (_isArray3) { + if (_i3 >= _iterator3.length) break; + _ref3 = _iterator3[_i3++]; + } else { + _i3 = _iterator3.next(); + if (_i3.done) break; + _ref3 = _i3.value; + } + + var role = _ref3; + + if (!role.server.memberMap[member.id]) { + return Promise.reject(new Error("member not in server")); + } + roleIDS.concat(role.id); + } + + return _superagent2["default"].patch(_Constants.Endpoints.SERVER_MEMBERS(role.server.id) + "/" + member.id).set("authorization", this.token).send({ + roles: roleIDS + }).end(); + }; + //def removeMemberFromRole InternalClient.prototype.removeMemberFromRole = function removeMemberFromRole(member, role) { @@ -682,7 +724,54 @@ var InternalClient = (function () { for (var item in roleIDS) { if (roleIDS[item] === role.id) { roleIDS.splice(item, 1); - //missing break? + break; + } + } + + return _superagent2["default"].patch(_Constants.Endpoints.SERVER_MEMBERS(role.server.id) + "/" + member.id).set("authorization", this.token).send({ + roles: roleIDS + }).end(); + }; + + //def removeMemberFromRoles + + InternalClient.prototype.removeMemberFromRoles = function removeMemberFromRoles(member, roles) { + member = this.resolver.resolveUser(member); + + if (!member) { + return Promise.reject(new Error("member not in server")); + } + + if (!Array.isArray(roles) || roles.length === 0) { + return Promise.reject(new Error("invalid array of roles")); + } + + var roleIDS = roles[0].server.memberMap[member.id].roles.map(function (r) { + return r.id; + }); + + for (var _iterator4 = roles, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { + var _ref4; + + if (_isArray4) { + if (_i4 >= _iterator4.length) break; + _ref4 = _iterator4[_i4++]; + } else { + _i4 = _iterator4.next(); + if (_i4.done) break; + _ref4 = _i4.value; + } + + var role = _ref4; + + if (!role.server.memberMap[member.id]) { + return Promise.reject(new Error("member not in server")); + } + for (var item in roleIDS) { + if (roleIDS[item] === role.id) { + roleIDS.splice(item, 1); + break; + } } } @@ -1140,19 +1229,19 @@ var InternalClient = (function () { var server = self.servers.get("id", data.id); if (server) { - for (var _iterator3 = server.channels, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { - var _ref3; + for (var _iterator5 = server.channels, _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { + var _ref5; - if (_isArray3) { - if (_i3 >= _iterator3.length) break; - _ref3 = _iterator3[_i3++]; + if (_isArray5) { + if (_i5 >= _iterator5.length) break; + _ref5 = _iterator5[_i5++]; } else { - _i3 = _iterator3.next(); - if (_i3.done) break; - _ref3 = _i3.value; + _i5 = _iterator5.next(); + if (_i5.done) break; + _ref5 = _i5.value; } - var channel = _ref3; + var channel = _ref5; self.channels.remove(channel); } diff --git a/lib/Voice/AudioEncoder.js b/lib/Voice/AudioEncoder.js index 4b8489b9..1abdcf20 100644 --- a/lib/Voice/AudioEncoder.js +++ b/lib/Voice/AudioEncoder.js @@ -22,7 +22,7 @@ var AudioEncoder = (function () { _classCallCheck(this, AudioEncoder); if (opus) { - this.opus = new opus.OpusEncoder(48000, 1); + this.opus = new opus.OpusEncoder(48000, 2); } this.choice = false; } @@ -67,8 +67,7 @@ var AudioEncoder = (function () { var self = this; return new Promise(function (resolve, reject) { - var enc = _child_process2["default"].spawn(self.getCommand(), ["-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", "-"]); + var enc = _child_process2["default"].spawn(self.getCommand(), ['-i', "-", '-f', 's16le', '-ar', '48000', '-ac', 2, 'pipe:1']); stream.pipe(enc.stdin); @@ -76,12 +75,14 @@ var AudioEncoder = (function () { callback(null, { proc: enc, stream: enc.stdout, - instream: stream + instream: stream, + channels: 2 }); resolve({ proc: enc, stream: enc.stdout, - instream: stream + instream: stream, + channels: 2 }); }); @@ -102,26 +103,29 @@ var AudioEncoder = (function () { var self = this; return new Promise(function (resolve, reject) { - var enc = _child_process2["default"].spawn(self.getCommand(), ["-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]); + var enc = _child_process2["default"].spawn(self.getCommand(), ['-i', file, '-f', 's16le', '-ar', '48000', '-ac', 2, 'pipe:1']); enc.stdout.once("readable", function () { callback(null, { proc: enc, - stream: enc.stdout + stream: enc.stdout, + channels: 2 }); resolve({ proc: enc, - stream: enc.stdout + stream: enc.stdout, + channels: 2 }); }); enc.stdout.on("end", function () { + console.log("end"); callback("end"); reject("end"); }); enc.stdout.on("close", function () { + console.log("close"); callback("close"); reject("close"); }); diff --git a/lib/Voice/VoiceConnection.js b/lib/Voice/VoiceConnection.js index ac43034e..96bde907 100644 --- a/lib/Voice/VoiceConnection.js +++ b/lib/Voice/VoiceConnection.js @@ -103,6 +103,7 @@ var VoiceConnection = (function (_EventEmitter) { }; VoiceConnection.prototype.playStream = function playStream(stream) { + var channels = arguments.length <= 1 || arguments[1] === undefined ? 2 : arguments[1]; var self = this; @@ -122,21 +123,21 @@ var VoiceConnection = (function (_EventEmitter) { self.playingIntent = retStream; function send() { - if (!self.playingIntent || !self.playing) { self.setSpeaking(false); retStream.emit("end"); - self; + console.log("ending 1"); return; } try { - var buffer = stream.read(1920); + var buffer = stream.read(1920 * channels); if (!buffer) { if (onWarning) { retStream.emit("end"); self.setSpeaking(false); + console.log("ending 2"); return; } else { onWarning = true; @@ -145,8 +146,8 @@ var VoiceConnection = (function (_EventEmitter) { } } - if (buffer.length !== 1920) { - var newBuffer = new Buffer(1920).fill(0); + if (buffer.length !== 1920 * channels) { + var newBuffer = new Buffer(1920 * channels).fill(0); buffer.copy(newBuffer); buffer = newBuffer; } @@ -238,7 +239,7 @@ var VoiceConnection = (function (_EventEmitter) { 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); + var intent = self.playStream(data.stream, 2); resolve(intent); callback(null, intent); }); diff --git a/src/Voice/AudioEncoder.js b/src/Voice/AudioEncoder.js index 8e1f86b2..99546ffe 100644 --- a/src/Voice/AudioEncoder.js +++ b/src/Voice/AudioEncoder.js @@ -3,36 +3,36 @@ import cpoc from "child_process"; var opus; -try{ +try { opus = require("node-opus"); -}catch(e){ +} catch (e) { // no opus! } export default class AudioEncoder { - constructor(){ - if(opus){ - this.opus = new opus.OpusEncoder(48000, 1); + constructor() { + if (opus) { + this.opus = new opus.OpusEncoder(48000, 2); } this.choice = false; } - opusBuffer(buffer){ + opusBuffer(buffer) { return this.opus.encode(buffer, 1920); } - getCommand(force){ + getCommand(force) { - if(this.choice && force) + if (this.choice && force) return choice; var choices = ["avconv", "ffmpeg"]; - for(var choice of choices){ + for (var choice of choices) { var p = cpoc.spawnSync(choice); - if(!p.error){ + if (!p.error) { this.choice = choice; return choice; } @@ -41,74 +41,78 @@ export default class AudioEncoder { return "help"; } - encodeStream(stream, callback=function(err, buffer){}){ + encodeStream(stream, callback = function (err, buffer) { }) { var self = this; return new Promise((resolve, reject) => { - var enc = cpoc.spawn(self.getCommand() , [ - "-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", "-" + var enc = cpoc.spawn(self.getCommand(), [ + '-i', "-", + '-f', 's16le', + '-ar', '48000', + '-ac', 2, + 'pipe:1' ]); stream.pipe(enc.stdin); - enc.stdout.once("readable", function() { + enc.stdout.once("readable", function () { callback(null, { - proc : enc, - stream : enc.stdout, - instream : stream + proc: enc, + stream: enc.stdout, + instream: stream, + channels : 2 }); resolve({ - proc : enc, - stream : enc.stdout, - instream : stream + proc: enc, + stream: enc.stdout, + instream: stream, + channels : 2 }); }); - enc.stdout.on("end", function() { + enc.stdout.on("end", function () { callback("end"); reject("end"); }); - enc.stdout.on("close", function() { + enc.stdout.on("close", function () { callback("close"); reject("close"); }); }); } - encodeFile(file, callback=function(err, buffer){}){ + encodeFile(file, callback = function (err, buffer) { }) { var self = this; return new Promise((resolve, reject) => { - var enc = cpoc.spawn(self.getCommand() , [ - "-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 + var enc = cpoc.spawn(self.getCommand(), [ + '-i', file, + '-f', 's16le', + '-ar', '48000', + '-ac', 2, + 'pipe:1' ]); - enc.stdout.once("readable", function() { + enc.stdout.once("readable", function () { callback(null, { - proc : enc, - stream : enc.stdout + proc: enc, + stream: enc.stdout, + channels : 2 }); resolve({ - proc : enc, - stream : enc.stdout + proc: enc, + stream: enc.stdout, + channels : 2 }); }); - enc.stdout.on("end", function() { + enc.stdout.on("end", function () { + console.log("end"); callback("end"); reject("end"); }); - enc.stdout.on("close", function() { + enc.stdout.on("close", function () { + console.log("close"); callback("close"); reject("close"); }); diff --git a/src/Voice/VoiceConnection.js b/src/Voice/VoiceConnection.js index 4e4cb01b..ecdc9356 100644 --- a/src/Voice/VoiceConnection.js +++ b/src/Voice/VoiceConnection.js @@ -73,7 +73,7 @@ export default class VoiceConnection extends EventEmitter { } } - playStream(stream) { + playStream(stream, channels=2) { var self = this; @@ -93,21 +93,21 @@ export default class VoiceConnection extends EventEmitter { self.playingIntent = retStream; function send() { - if (!self.playingIntent || !self.playing) { self.setSpeaking(false); retStream.emit("end"); - self + console.log("ending 1"); return; } try { - var buffer = stream.read(1920); + var buffer = stream.read(1920 * channels); if (!buffer) { if (onWarning) { retStream.emit("end"); self.setSpeaking(false); + console.log("ending 2"); return; } else { onWarning = true; @@ -116,8 +116,8 @@ export default class VoiceConnection extends EventEmitter { } } - if(buffer.length !== 1920) { - var newBuffer = new Buffer(1920).fill(0); + if(buffer.length !== 1920 * channels) { + var newBuffer = new Buffer(1920 * channels).fill(0); buffer.copy(newBuffer); buffer = newBuffer; } @@ -213,7 +213,7 @@ export default class VoiceConnection extends EventEmitter { .catch(error) .then(data => { self.streamProc = data.proc; - var intent = self.playStream(data.stream); + var intent = self.playStream(data.stream, 2); resolve(intent); callback(null, intent); diff --git a/test/msgbot.js b/test/msgbot.js new file mode 100644 index 00000000..a651f66b --- /dev/null +++ b/test/msgbot.js @@ -0,0 +1,40 @@ +/* global describe */ +/* global process */ + +var Discord = require("../"); +var client = new Discord.Client(); +var request = require("superagent"); + +client.on("ready", () => { + console.log("ready"); +}); + +client.on("message", msg => { + + if(!msg.sender.equals(client.user)) + console.log("received message from " + msg.sender.username); + + if (msg.content === "$bind") { + msg.channel.server.channels.get("type", "voice").join(); + } + + if (msg.content.startsWith("$play")) { + var url = msg.content.split(" ")[1]; + + client.voiceConnection.playFile(url); + + console.log(request.get(url).end()); + + } + + if (msg.content === "$$$") { + client.sendMessage(msg.sender, "hi!"); + } + +}); + +console.log("INIT"); + +client.on("debug", console.log) + +client.login(process.env["ds_email"], process.env["ds_password"]).catch(console.log); \ No newline at end of file