625 lines
19 KiB
JavaScript
625 lines
19 KiB
JavaScript
//Discord Credentials
|
|
var discordusr = ""; //fill here
|
|
var discordpss = ""; //fill here
|
|
|
|
//Database connection string (PostgreSQL only, MySQL not supported)
|
|
var conString = "postgres://"; //fill here
|
|
// postgres://username:password@localhost/databasename
|
|
|
|
//Username in unix-like environment (don't remove the slashes)
|
|
var unixuserfoldername='/kiror/';
|
|
|
|
//Forwarded ports you need have (edit if yours are different)
|
|
var httpport='8000';
|
|
var sshport ='8003';
|
|
|
|
//The user that is running this on his machine
|
|
var absolutemaster=''; //Put your userID here
|
|
|
|
//The trusted users who can use annoying commands
|
|
var masters = [absolutemaster,'']; //add people who can control the bot
|
|
|
|
//The version string
|
|
var botver='0.0.2';
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
var Discordie = require("discordie");
|
|
var Events = Discordie.Events;
|
|
|
|
var client = new Discordie();
|
|
|
|
var cpath = require('path').dirname(require.main.filename);
|
|
var os = require("os");
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
var child_process = require('child_process');
|
|
var request = require('request');
|
|
var pg = require('pg');
|
|
var exec = require('child_process').exec;
|
|
|
|
var musicfile="test.mp3"; //just an initial file (practically, a placeholder)
|
|
|
|
var audiofolder='audio_tmp/';
|
|
|
|
exec('mkdir -p '+audiofolder,function (error, stdout, stderr){;});
|
|
exec('mkdir -p /var/www/html/discord_logs/audios/',function (error, stdout, stderr){;});
|
|
|
|
function getNowUtc(){
|
|
v=new Date();
|
|
return v.getTime()/1000;
|
|
}
|
|
|
|
function randomIntInc (low, high) {//http://blog.tompawlak.org/generate-random-values-nodejs-javascript
|
|
return Math.floor(Math.random() * (high - low + 1) + low);
|
|
}
|
|
|
|
function createTableTxt(){
|
|
q='CREATE TABLE IF NOT EXISTS kiror_discordbot_text (\
|
|
seq SERIAL PRIMARY KEY,\
|
|
mde INT,\
|
|
uid BIGINT,\
|
|
tid BIGINT,\
|
|
sid BIGINT,\
|
|
mid BIGINT,\
|
|
unm TEXT,\
|
|
tnm TEXT,\
|
|
snm TEXT,\
|
|
msg TEXT,\
|
|
att TEXT,\
|
|
ts FLOAT\
|
|
);';
|
|
db.query(q, function(err, result) {
|
|
if(err) {
|
|
return console.error('Error in create table query: kiror_discordbot_text', err);
|
|
}
|
|
});
|
|
}
|
|
|
|
function createTableVoice(){
|
|
q='CREATE TABLE IF NOT EXISTS kiror_discordbot_voice (\
|
|
seq SERIAL PRIMARY KEY,\
|
|
ts FLOAT,\
|
|
name TEXT,\
|
|
uid BIGINT,\
|
|
ssrc INT,\
|
|
pts FLOAT,\
|
|
pseq BIGINT\
|
|
);';
|
|
//(ts,name,uid,bytes,ssrc,pts,pseq)
|
|
db.query(q, function(err, result) {
|
|
if(err) {
|
|
return console.error('Error in create table query: kiror_discordbot_voice', err);
|
|
}
|
|
});
|
|
}
|
|
|
|
var distractions=true;
|
|
|
|
var db = new pg.Client(conString);
|
|
db.connect(function(err) {
|
|
if(err) {
|
|
setInterval(process.exit, 2000);
|
|
return console.error('could not connect to postgres', err);
|
|
}
|
|
db.query('SELECT NOW() AS "theTime"', function(err, result) {
|
|
if(err) {
|
|
setInterval(process.exit, 2000);
|
|
return console.error('error running query', err);
|
|
}
|
|
console.log(result.rows[0].theTime);
|
|
//output: Fri Jan 22 2016 15:45:52 GMT-0200 (BRST)
|
|
});
|
|
createTableTxt();
|
|
createTableVoice();
|
|
});
|
|
|
|
function storeTxtMsg(mode,uid,unm,sid,snm,tid,tnm,mid,msg,att){
|
|
createTableTxt();
|
|
now = getNowUtc();
|
|
//console.log(now);
|
|
q='INSERT INTO kiror_discordbot_text (mde,uid,tid,sid,unm,tnm,snm,mid,msg,att,ts) VALUES\
|
|
(($1)::int, ($2)::bigint, ($3)::bigint, ($4)::bigint, ($5)::text, ($6)::text, ($7)::text,\
|
|
($8)::bigint, ($9)::text, ($10)::text, ($11)::float);';
|
|
arr=[mode,uid,tid,sid,unm,tnm,snm,mid,msg,JSON.stringify(att),now];
|
|
//JSON.stringify
|
|
// dual
|
|
//JSON.parse
|
|
db.query(q,arr,function(err, result) {
|
|
if(err) {
|
|
return console.error('Error while inserting in table: kiror_discordbot_text', err);
|
|
}
|
|
});
|
|
}
|
|
|
|
function storeVoiceMsg(ts,name,uid,ssrc,pts,pseq){
|
|
createTableVoice();
|
|
now = getNowUtc();
|
|
//console.log(now);
|
|
q='INSERT INTO kiror_discordbot_voice (ts,name,uid,ssrc,pts,pseq) VALUES\
|
|
(($1)::float, ($2)::text, ($3)::bigint, ($4)::int, ($5)::float, ($6)::bigint);';
|
|
arr=[ts,name,uid,ssrc,pts,pseq];
|
|
//JSON.stringify
|
|
// dual
|
|
//JSON.parse
|
|
db.query(q,arr,function(err, result) {
|
|
if(err) {
|
|
return console.error('Error while inserting in table: kiror_discordbot_voice', err);
|
|
}
|
|
});
|
|
}
|
|
|
|
function discordLogin(){
|
|
client.connect({
|
|
email: discordusr,
|
|
password: discordpss
|
|
});
|
|
}
|
|
|
|
|
|
client.Dispatcher.on(Events.GATEWAY_READY, e => {
|
|
console.log(client.User.username + ' ('+client.User.id+')');
|
|
});
|
|
|
|
function isInArray(haystack,needle) {
|
|
return haystack.indexOf(needle) > -1;
|
|
}
|
|
|
|
function stringStartsWith (string, prefix) {
|
|
return string.slice(0, prefix.length) == prefix;
|
|
}
|
|
|
|
iplookupurl='http://www.meuip.com.br/index.php';
|
|
|
|
var voiceconn = undefined;
|
|
|
|
var hoster = false;
|
|
|
|
discordLogin();
|
|
|
|
client.Dispatcher.on(Events.DISCONNECTED, e => {
|
|
setInterval(discordLogin, 5000);
|
|
});
|
|
|
|
client.Dispatcher.on(Discordie.Events.VOICE_CONNECTED, (e) => {
|
|
e.voiceConnection.getDecoder()
|
|
.onPacketDecoded = (packet) => {
|
|
const user = e.voiceConnection.ssrcToMember(packet.ssrc);
|
|
if (!user) return;
|
|
|
|
var ts = getNowUtc();
|
|
var name = user ? user.username : null;
|
|
var uid = user ? user.id : null;
|
|
var bytes = packet.chunk;
|
|
var ssrc = packet.ssrc;
|
|
var pts = packet.timestamp/100000;
|
|
var pseq = packet.seq;
|
|
//(ts,name,uid,bytes,ssrc,pts,pseq)
|
|
|
|
//console.log(e);
|
|
//console.log(packet)
|
|
|
|
afn=(uid+'_'+pseq+'.s16le');
|
|
fs.writeFile(audiofolder+afn,bytes,function(err){
|
|
if(err) {
|
|
return console.log('Error writing file... '+err);
|
|
}else{
|
|
cmd='ln -s '+cpath+'/'+audiofolder+afn+' /var/www/html/discord_logs/audios/'+afn;
|
|
exec(cmd,function (error, stdout, stderr){
|
|
//console.log([error,stdout,stderr])
|
|
;});
|
|
storeVoiceMsg(ts,name,uid,ssrc,pts,pseq);
|
|
}
|
|
});
|
|
|
|
}
|
|
});
|
|
|
|
client.Dispatcher.on(Events.MESSAGE_DELETE, e => {
|
|
//(mode,uid,unm,sid,snm,tid,tnm,mid,msg,att)
|
|
try{mid_md=(e.messageId);}catch(err){mid_md=null};
|
|
try{cnt_md=(e.message.content);}catch(err){cnt_md=null};
|
|
try{att_md=(e.message.attachments);}catch(err){att_md=null};
|
|
try{uid_md=(e.message.author.id);}catch(err){uid_md=null};
|
|
try{unm_md=(e.message.author.username);}catch(err){unm_md=null};
|
|
try{cid_md=(e.channelId);}catch(err){cid_md=null};
|
|
try{cnm_md=(e.message.channel.name);}catch(err){cnm_md=null};
|
|
try{sid_md=(e.message.channel.guild.id);}catch(err){sid_md=null};
|
|
try{snm_md=(e.message.channel.guild.name);}catch(err){snm_md=null};
|
|
//(2,uid_md,unm_md,sid_md,snm_md,cid_md,cnm_md,mid_md,cnt_md,att_md)
|
|
storeTxtMsg(2,uid_md,unm_md,sid_md,snm_md,cid_md,cnm_md,mid_md,cnt_md,att_md);
|
|
}
|
|
);
|
|
|
|
client.Dispatcher.on(Events.MESSAGE_UPDATE, e => {
|
|
{
|
|
//(mode,uid,unm,sid,snm,tid,tnm,mid,msg,att)
|
|
mid_mu=(e.message.id);
|
|
cnt_mu=(e.message.content);
|
|
att_mu=(e.message.attachments);
|
|
uid_mu=(e.message.author.id);
|
|
unm_mu=(e.message.author.username);
|
|
cid_mu=(e.message.channel.id);
|
|
cnm_mu=(e.message.channel.name);
|
|
sid_mu=(e.message.channel.guild.id);
|
|
snm_mu=(e.message.channel.guild.name);
|
|
//(1,uid_mu,unm_mu,sid_mu,snm_mu,cid_mu,cnm_mu,mid_mu,cnt_mu,att_mu)
|
|
storeTxtMsg(1,uid_mu,unm_mu,sid_mu,snm_mu,cid_mu,cnm_mu,mid_mu,cnt_mu,att_mu);
|
|
}
|
|
});
|
|
|
|
client.Dispatcher.on(Events.MESSAGE_CREATE, e => {
|
|
if (!e.message.channel.is_private){
|
|
//(mode,uid,unm,sid,snm,tid,tnm,mid,msg,att)
|
|
mid_mc=(e.message.id);
|
|
cnt_mc=(e.message.content);
|
|
att_mc=(e.message.attachments);
|
|
uid_mc=(e.message.author.id);
|
|
unm_mc=(e.message.author.username);
|
|
cid_mc=(e.message.channel.id);
|
|
cnm_mc=(e.message.channel.name);
|
|
sid_mc=(e.message.channel.guild.id);
|
|
snm_mc=(e.message.channel.guild.name);
|
|
//(0,uid_mc,unm_mc,sid_mc,snm_mc,cid_mc,cnm_mc,mid_mc,cnt_mc,att_mc)
|
|
storeTxtMsg(0,uid_mc,unm_mc,sid_mc,snm_mc,cid_mc,cnm_mc,mid_mc,cnt_mc,att_mc);
|
|
}
|
|
if(e.message.author.id==client.User.id){;}
|
|
else if (e.message.channel.is_private){if(randomIntInc(1,3)==3){return;}else{e.message.channel.sendMessage('Nothing to talk with you.');}}
|
|
else if (stringStartsWith(e.message.content,'<@'+client.User.id+'>')){
|
|
distractionsallowed = (distractions || isInArray(masters,e.message.author.id));
|
|
hoster=(absolutemaster==e.message.author.id);
|
|
function replyBack(msg){
|
|
e.message.channel.sendMessage(msg);
|
|
};
|
|
var command = (e.message.content).slice(((('<@'+client.User.id+'>').length)+1));
|
|
if (distractionsallowed && command=='help'){
|
|
replyBack('HELP TEXT:\n\
|
|
`@'+client.User.username+' help`: You get this text.\n\
|
|
`@'+client.User.username+' version`: I show you my version.\n\
|
|
`@'+client.User.username+' suicide`: I close myself.\n\
|
|
`@'+client.User.username+' amnesia`: I forget everything.\n\
|
|
`@'+client.User.username+' ip`: You get some ways to connect to me.\n\
|
|
`@'+client.User.username+' list music`: I say randomly 20 musics I have.\n\
|
|
`@'+client.User.username+' list voice`: I say all voice channels I\'m seeing.\n\
|
|
`@'+client.User.username+' join voice XXXX`: I join the voice channel you asked.\n\
|
|
`@'+client.User.username+' leave voice XXXX`: I leave the voice channel you asked.\n\
|
|
`@'+client.User.username+' disk status`: You see how much space left I have in disk.\n\
|
|
`@'+client.User.username+' download`: You get a download link for downloading the logs I have.\n\
|
|
`@'+client.User.username+' play XXXX`: I play the audio file in the voice channel I am.\n\
|
|
`@'+client.User.username+' stop`: I stop the music.\n\
|
|
`@'+client.User.username+' record`: I\'m already doing it.\n\
|
|
`@'+client.User.username+' distractions`: It\'s a switch.\n\
|
|
`@'+client.User.username+' easteregg`: Does nothing.');return;
|
|
}
|
|
else if (distractionsallowed && command=='easteregg'){
|
|
replyBack('HELP TEXT -- PUBLIC FUNCTIONALITIES -- PAGE 2:\n\
|
|
`@'+client.User.username+' help`: Returns to fist page.\n\
|
|
`@'+client.User.username+' supercow`: An advice.\n\
|
|
`@'+client.User.username+' roll dXX`: Rolls a dice with `XX` faces.\n\
|
|
');return;
|
|
}
|
|
else if (distractionsallowed && command=='supercow'){
|
|
replyBack('```SH\n\
|
|
s\n\
|
|
u (__) \n\
|
|
p (oo) \n\
|
|
e /------\\/ \n\
|
|
r / | || \n\
|
|
c * /\\---/\\ \n\
|
|
o ~~ ~~ \n\
|
|
w ..."Have you mooed today?"...```');return;
|
|
}
|
|
else if (distractionsallowed && stringStartsWith(command,'roll d')){
|
|
dice=command.substring(6);
|
|
di=parseInt(dice);
|
|
if (dice=='k'){
|
|
di=1000;
|
|
}
|
|
if (dice=='m'){
|
|
di=1000000;
|
|
}
|
|
if (di==null || di==undefined || di<=1){
|
|
replyBack('I don\'t know a dice with "'+dice+'" faces.');
|
|
}
|
|
res=randomIntInc(1,di);
|
|
replyBack('d'+di+'... '+res+'!');
|
|
return;
|
|
}
|
|
////////////////////NORMAL USERS BARRIER////////////////////////
|
|
else if (!isInArray(masters,e.message.author.id)){
|
|
if (distractions){
|
|
replyBack("<@"+e.message.author.id+"> You're not my master!");
|
|
}
|
|
return;
|
|
}
|
|
else if (command=='distractions'){
|
|
distractions=!distractions;
|
|
replyBack('Now, distractions are '+{true:'on',false:'off'}[distractions]+'.');
|
|
}
|
|
else if (command=='version'){
|
|
replyBack('kiror\'s bot v.'+botver+"\n"+'NodeJS running on `'+os.hostname()+'`.');
|
|
}
|
|
else if (command=='ip'){
|
|
request(iplookupurl, function (error, response, body) {
|
|
if (!error && response.statusCode == 200) {
|
|
ip=body.split('Meu IP:')[1].split('</div>')[0].split('>');
|
|
ip=ip[ip.length-1].trim();
|
|
replyBack(ip+"\nhttp://"+ip+':'+httpport+'/'+"\n```SH\nssh kiror@"+ip+" -p "+sshport+"```");
|
|
}
|
|
else {replyBack('Some error happened...');}
|
|
})
|
|
}
|
|
else if (command=='suicide'){
|
|
replyBack('Okay, so...');
|
|
if(!hoster){
|
|
replyBack('Wait... you can\'t revive me. Better not.');
|
|
return;
|
|
}
|
|
db.end();
|
|
client.disconnect();
|
|
setInterval(process.exit, 2000);
|
|
return;
|
|
}
|
|
else if (command=='amnesia'){
|
|
if(!hoster){
|
|
replyBack('Wait... you\'re not the one who is running me. Better not.');
|
|
return;
|
|
}
|
|
cmd='rm -rf /var/www/html/discord_logs/; rm -rf '+audiofolder+' ; rm *.json; rm *.lzma; psql -c"drop table kiror_discordbot_text;drop table kiror_discordbot_voice;"';
|
|
exec(cmd,function (error, stdout, stderr){
|
|
exec('mkdir -p '+audiofolder,function (error, stdout, stderr){;});
|
|
exec('mkdir -p /var/www/html/discord_logs/audios/',function (error, stdout, stderr){;});
|
|
replyBack('Who am I?');
|
|
});
|
|
return;
|
|
}
|
|
else if (command=='list voice'){
|
|
var ch2 = e.message.channel.guild.voiceChannels;
|
|
var ch = {};
|
|
for(var k in ch2){
|
|
if(ch2[k].type=='voice'){
|
|
ch[ch2[k].name]=[ch2[k].id,ch2[k]];
|
|
}
|
|
}
|
|
var s = "\n";
|
|
for(var k in ch){
|
|
s+="`"+ch[k][0]+"` = <#"+ch[k][0]+"> (`"+k+"`)\n";
|
|
}
|
|
replyBack(s);
|
|
}
|
|
else if (stringStartsWith(command,'join voice ')){
|
|
var k = command.substring(11);
|
|
var ch2 = e.message.channel.guild.voiceChannels;
|
|
var ch = {};
|
|
for(var j in ch2){
|
|
if(ch2[j].type=='voice'){
|
|
ch[ch2[j].name]=[ch2[j].id,ch2[j]];
|
|
}
|
|
}
|
|
var vcid = ch[k];
|
|
if(vcid==undefined){
|
|
replyBack('I don\'t know what voice channel is `'+k+'`.');
|
|
}else{
|
|
vcid=vcid[1];
|
|
replyBack('Joining...');
|
|
if(vcid.joined){vcid.leave();};
|
|
vcid.join()//.then(v => play(v));
|
|
voiceconn=vcid;
|
|
}
|
|
}
|
|
else if (stringStartsWith(command,'leave voice ')){
|
|
voiceconn=undefined;
|
|
var k = command.substring(12);
|
|
var ch2 = e.message.channel.guild.voiceChannels;
|
|
var ch = {};
|
|
for(var j in ch2){
|
|
if(ch2[j].type=='voice'){
|
|
ch[ch2[j].name]=[ch2[j].id,ch2[j]];
|
|
}
|
|
}
|
|
var vcid = ch[k];
|
|
if(vcid==undefined){
|
|
replyBack('I don\'t know what voice channel is `'+k+'`.');
|
|
}else{
|
|
vcid=vcid[1];
|
|
replyBack('Leaving...');
|
|
if(vcid.joined){vcid.leave();};
|
|
}
|
|
}
|
|
else if (stringStartsWith(command,'play ')){
|
|
if (voiceconn==undefined) {replyBack('Not connected to a voice channel.');return;}
|
|
var k = command.substring(5);
|
|
if(fs.lstatSync('/home'+unixuserfoldername+'Music/'+k).isFile()){
|
|
musicfile = '/home'+unixuserfoldername+'Music/'+k;
|
|
play();
|
|
}
|
|
else{replyBack('File not found.')}
|
|
}
|
|
else if (stringStartsWith(command,'stop')){
|
|
if (voiceconn==undefined) {replyBack('Not connected to a voice channel.');return;}
|
|
stop();
|
|
replyBack('If I was playing an audio before, I stopped now.');
|
|
}
|
|
else if (stringStartsWith(command,'record')){
|
|
if (voiceconn==undefined) {replyBack('Not connected to a voice channel.');return;}
|
|
stop();
|
|
replyBack('I am always recording every place I visit.');
|
|
}
|
|
else if (command == 'disk status'){
|
|
term="df | grep -e ' /$' | awk '{print \"Free space: \" $4/1024 \"MiB \\nDisk usage: \" $5 \" full.\"}'";
|
|
exec(term,function (error, stdout, stderr){
|
|
replyBack(stdout);
|
|
});
|
|
}
|
|
else if (stringStartsWith(command,'download')){
|
|
fnn='discord_log_'+Math.floor(getNowUtc());
|
|
fname=fnn+'.json';
|
|
db.query('SELECT * FROM kiror_discordbot_text',function(err, resulttxt) {
|
|
db.query('SELECT * FROM kiror_discordbot_voice',function(err, resultvc) {
|
|
fcont=JSON.stringify({'text':resulttxt, 'voice':resultvc});
|
|
fs.writeFile(fname, fcont, function(err) {
|
|
if(err) {
|
|
replyBack('Error while writing file... something is not okay.');
|
|
return;
|
|
}
|
|
});
|
|
term1='j="$(pwd)"; mkdir -p /var/www/html/discord_logs/; ln -s $j/'+fname+' /var/www/html/discord_logs/'+fname;
|
|
exec(term1,function (error, stdout, stderr){
|
|
term2='tar caf '+fnn+'.tar.lzma '+fname+' '+audiofolder;
|
|
exec(term2,function (error, stdout, stderr){
|
|
term3='j="$(pwd)"; ln -s $j/'+fnn+'.tar.lzma'+' /var/www/html/discord_logs/'+fnn+'.tar.lzma';
|
|
exec(term3,function (error, stdout, stderr){
|
|
;});
|
|
;});
|
|
;});
|
|
request(iplookupurl, function (error, response, body) {
|
|
if (!error && response.statusCode == 200) {
|
|
ip=body.split('Meu IP:')[1].split('</div>')[0].split('>');
|
|
ip=ip[ip.length-1].trim();
|
|
replyBack("Download link: http://"+ip+':'+httpport+'/discord_logs/'+fnn+'.tar.lzma'+"\n"+
|
|
"Or visit http://"+ip+':'+httpport+'/discord_logs/ to see all previous undeleted logs.');
|
|
}
|
|
else {replyBack('Some error happened...');}
|
|
})
|
|
});
|
|
});
|
|
}
|
|
else if (stringStartsWith(command,'list music')){
|
|
fs.readdir('/home'+unixuserfoldername+'Music', function (err, files){
|
|
pick=20;
|
|
arr=[];
|
|
while(pick>0){
|
|
rd=randomIntInc(0,files.length-1);
|
|
arr.push(rd);
|
|
pick-=1;
|
|
}
|
|
s='';
|
|
for (var mus in arr){
|
|
s+='No. '+arr[mus]+': `@'+client.User.username+' play '+files[arr[mus]]+'`\n';
|
|
}
|
|
s+='You can repeat this command to see more musics (randomly).'
|
|
replyBack(s);
|
|
});
|
|
}
|
|
else {
|
|
replyBack('I don\'t know what you want... try `help`.');
|
|
}
|
|
}
|
|
});
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////_FOR_MUSIC_PLAYING_/////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// Copy-paste from https://github.com/qeled/discordie/blob/master/examples/massive2.js ////////////
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function getConverter(args, options) {
|
|
var binaries = [
|
|
'ffmpeg',
|
|
'ffmpeg.exe',
|
|
'avconv',
|
|
'avconv.exe'
|
|
];
|
|
|
|
var paths = process.env.PATH.split(path.delimiter).concat(["."]);
|
|
|
|
for (var name of binaries) {
|
|
for (var p of paths) {
|
|
var binary = p + path.sep + name;
|
|
if (!fs.existsSync(binary)) continue;
|
|
return child_process.spawn(name, args, options);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
var ffmpeg = null;
|
|
function stop() {
|
|
stopPlaying = true;
|
|
if (!ffmpeg) return;
|
|
ffmpeg.kill();
|
|
ffmpeg = null;
|
|
}
|
|
|
|
var stopPlaying = false;
|
|
function play(voiceConnectionInfo) {
|
|
stopPlaying = false;
|
|
|
|
var sampleRate = 48000;
|
|
var bitDepth = 16;
|
|
var channels = 1;
|
|
|
|
if (ffmpeg) ffmpeg.kill();
|
|
|
|
ffmpeg = getConverter([
|
|
"-re",
|
|
"-i", musicfile,
|
|
"-f", "s16le",
|
|
"-ar", sampleRate,
|
|
"-ac", channels,
|
|
"-"
|
|
], {stdio: ['pipe', 'pipe', 'ignore']});
|
|
if (!ffmpeg) return console.log("ffmpeg/avconv not found");
|
|
|
|
var _ffmpeg = ffmpeg;
|
|
var ff = ffmpeg.stdout;
|
|
|
|
// note: discordie encoder does resampling if rate != 48000
|
|
var options = {
|
|
frameDuration: 60,
|
|
sampleRate: sampleRate,
|
|
channels: channels,
|
|
float: false,
|
|
|
|
multiThreadedVoice: true
|
|
};
|
|
|
|
const frameDuration = 60;
|
|
|
|
var readSize =
|
|
sampleRate / 1000 *
|
|
options.frameDuration *
|
|
bitDepth / 8 *
|
|
channels;
|
|
|
|
ff.once('readable', function() {
|
|
if(!client.VoiceConnections.length) {
|
|
return console.log("Voice not connected");
|
|
}
|
|
|
|
if(!voiceConnectionInfo) {
|
|
// get first if not specified
|
|
voiceConnectionInfo = client.VoiceConnections[0];
|
|
}
|
|
var voiceConnection = voiceConnectionInfo.voiceConnection;
|
|
|
|
// one encoder per voice connection
|
|
var encoder = voiceConnection.getEncoder(options);
|
|
|
|
const needBuffer = () => encoder.onNeedBuffer();
|
|
encoder.onNeedBuffer = function() {
|
|
var chunk = ff.read(readSize);
|
|
|
|
if (_ffmpeg.killed) return;
|
|
if (stopPlaying) return stop();
|
|
|
|
// delay the packet if no data buffered
|
|
if (!chunk) return setTimeout(needBuffer, options.frameDuration);
|
|
|
|
var sampleCount = readSize / channels / (bitDepth / 8);
|
|
encoder.enqueue(chunk, sampleCount);
|
|
};
|
|
|
|
needBuffer();
|
|
});
|
|
|
|
ff.once('end', () => {
|
|
if (stopPlaying) return;
|
|
setTimeout(play, 100, voiceConnectionInfo);
|
|
});
|
|
}
|
|
|
|
|