automation wip
This commit is contained in:
23
config.json
23
config.json
@@ -25,7 +25,30 @@
|
|||||||
"websocket": {
|
"websocket": {
|
||||||
"address": "192.168.0.111:4444",
|
"address": "192.168.0.111:4444",
|
||||||
"password": "goodnewseveryone"
|
"password": "goodnewseveryone"
|
||||||
|
},
|
||||||
|
"availablePlaylists": [
|
||||||
|
{
|
||||||
|
"sceneItem": "ttas-segments",
|
||||||
|
"activity": "TTAS Segments !ttas",
|
||||||
|
"chatName": "TTAS Segments"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sceneItem": "room-grind",
|
||||||
|
"activity": "TTAS Room Grind !ttas",
|
||||||
|
"chatName": "TTAS Room Grind"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sceneItem": "gold-segments",
|
||||||
|
"activity": "Any% NMG Gold Segments",
|
||||||
|
"chatName": "NMG Golds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sceneItem": "personal-bests",
|
||||||
|
"activity": "Past Personal Bests",
|
||||||
|
"chatName": "Personal Bests"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"defaultPlaylist": "room-grind"
|
||||||
},
|
},
|
||||||
"debug": false
|
"debug": false
|
||||||
}
|
}
|
||||||
49
lib/util.js
Executable file
49
lib/util.js
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
// Converts seconds to human-readable time
|
||||||
|
String.prototype.toHHMMSS = function () {
|
||||||
|
let sec_num = parseInt(this, 10); // don't forget the second param
|
||||||
|
let hours = Math.floor(sec_num / 3600);
|
||||||
|
let minutes = Math.floor((sec_num - (hours * 3600)) / 60);
|
||||||
|
let seconds = sec_num - (hours * 3600) - (minutes * 60);
|
||||||
|
|
||||||
|
if (hours < 10) {hours = "0"+hours;}
|
||||||
|
if (minutes < 10) {minutes = "0"+minutes;}
|
||||||
|
if (seconds < 10) {seconds = "0"+seconds;}
|
||||||
|
return hours+':'+minutes+':'+seconds;
|
||||||
|
};
|
||||||
|
|
||||||
|
var exports = module.exports = {};
|
||||||
|
|
||||||
|
exports.asyncForEach = async function(array, callback) {
|
||||||
|
for (let index = 0; index < array.length; index++) {
|
||||||
|
await callback(array[index], index, array)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.range = function(start,stop) {
|
||||||
|
var result=[];
|
||||||
|
for (var idx=start.charCodeAt(0),end=stop.charCodeAt(0); idx <=end; ++idx){
|
||||||
|
result.push(String.fromCharCode(idx));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.randElement = function(arr) {
|
||||||
|
return arr[Math.floor(Math.random() * arr.length)];
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.sum = function(e) {
|
||||||
|
let sum = 0;
|
||||||
|
for (let i = 0; i < e.length; i++) {
|
||||||
|
sum += parseInt(e[i], 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.average = function(e) {
|
||||||
|
let sum = exports.sum(e);
|
||||||
|
|
||||||
|
let avg = sum / e.length;
|
||||||
|
|
||||||
|
return avg;
|
||||||
|
};
|
||||||
58
package-lock.json
generated
58
package-lock.json
generated
@@ -118,6 +118,15 @@
|
|||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||||
},
|
},
|
||||||
|
"cron-parser": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-KGfDDTjBIx85MnVYcdhLccoJH/7jcYW+5Z/t3Wsg2QlJhmmjf+97z+9sQftS71lopOYYapjEKEvmWaCsym5Z4g==",
|
||||||
|
"requires": {
|
||||||
|
"is-nan": "1.2.1",
|
||||||
|
"moment-timezone": "0.5.21"
|
||||||
|
}
|
||||||
|
},
|
||||||
"crypt": {
|
"crypt": {
|
||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
|
||||||
@@ -139,6 +148,14 @@
|
|||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"define-properties": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
|
||||||
|
"requires": {
|
||||||
|
"object-keys": "1.0.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"delayed-stream": {
|
"delayed-stream": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
@@ -264,6 +281,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
|
||||||
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
|
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
|
||||||
},
|
},
|
||||||
|
"is-nan": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.2.1.tgz",
|
||||||
|
"integrity": "sha1-n69ltvttskt/XAYoR16nH5iEAeI=",
|
||||||
|
"requires": {
|
||||||
|
"define-properties": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"is-typedarray": {
|
"is-typedarray": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||||
@@ -311,6 +336,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||||
},
|
},
|
||||||
|
"long-timeout": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz",
|
||||||
|
"integrity": "sha1-lyHXiLR+C8taJMLivuGg2lXatRQ="
|
||||||
|
},
|
||||||
"md5": {
|
"md5": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
|
||||||
@@ -331,6 +361,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
|
||||||
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
|
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
|
||||||
},
|
},
|
||||||
|
"moment-timezone": {
|
||||||
|
"version": "0.5.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.21.tgz",
|
||||||
|
"integrity": "sha512-j96bAh4otsgj3lKydm3K7kdtA3iKf2m6MY2iSYCzCm5a1zmHo1g+aK3068dDEeocLZQIS9kU8bsdQHLqEvgW0A==",
|
||||||
|
"requires": {
|
||||||
|
"moment": "2.22.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
@@ -361,11 +399,26 @@
|
|||||||
"ogg-packet": "1.0.0"
|
"ogg-packet": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node-schedule": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-NNwO9SUPjBwFmPh3vXiPVEhJLn4uqYmZYvJV358SRGM06BR4UoIqxJpeJwDDXB6atULsgQA97MfD1zMd5xsu+A==",
|
||||||
|
"requires": {
|
||||||
|
"cron-parser": "2.6.0",
|
||||||
|
"long-timeout": "0.1.1",
|
||||||
|
"sorted-array-functions": "1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"oauth-sign": {
|
"oauth-sign": {
|
||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
|
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
|
||||||
},
|
},
|
||||||
|
"object-keys": {
|
||||||
|
"version": "1.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
|
||||||
|
"integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag=="
|
||||||
|
},
|
||||||
"obs-websocket-js": {
|
"obs-websocket-js": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/obs-websocket-js/-/obs-websocket-js-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/obs-websocket-js/-/obs-websocket-js-1.2.0.tgz",
|
||||||
@@ -544,6 +597,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz",
|
||||||
"integrity": "sha512-NjxjITIj04Ffqid5lqr7XdgwM7X61c/Dns073Ly170bPQHLm6jkmelye/eglS++1nfTWktpP6Y2bFXjdPlQqdw=="
|
"integrity": "sha512-NjxjITIj04Ffqid5lqr7XdgwM7X61c/Dns073Ly170bPQHLm6jkmelye/eglS++1nfTWktpP6Y2bFXjdPlQqdw=="
|
||||||
},
|
},
|
||||||
|
"sorted-array-functions": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-sWpjPhIZJtqO77GN+LD8dDsDKcWZ9GCOJNqKzi1tvtjGIzwfoyuRH8S0psunmc6Z5P+qfDqztSbwYR5X/e1UTg=="
|
||||||
|
},
|
||||||
"sshpk": {
|
"sshpk": {
|
||||||
"version": "1.14.2",
|
"version": "1.14.2",
|
||||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
|
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"memcache": "^0.3.0",
|
"memcache": "^0.3.0",
|
||||||
"moment": "^2.22.2",
|
"moment": "^2.22.2",
|
||||||
"node-opus": "^0.2.9",
|
"node-opus": "^0.2.9",
|
||||||
|
"node-schedule": "^1.3.0",
|
||||||
"obs-websocket-js": "^1.2.0",
|
"obs-websocket-js": "^1.2.0",
|
||||||
"request": "^2.88.0"
|
"request": "^2.88.0"
|
||||||
},
|
},
|
||||||
|
|||||||
222
twitch.js
222
twitch.js
@@ -2,20 +2,22 @@
|
|||||||
* GHBot4Twitch
|
* GHBot4Twitch
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// @TODO: modularize OBS and Twitch code
|
||||||
// @TODO: Make the bot aware of what video is current active
|
// @TODO: Make the bot aware of what video is current active
|
||||||
// @TODO: Change video playlist source on an interval
|
// @TODO: Change video playlist source on an interval
|
||||||
|
// @TODO: Rotating background images (leftside)
|
||||||
|
|
||||||
|
|
||||||
// Import modules
|
// Import modules
|
||||||
const irc = require('irc');
|
const irc = require('irc');
|
||||||
const OBSWebSocket = require('obs-websocket-js');
|
const OBSWebSocket = require('obs-websocket-js');
|
||||||
|
const schedule = require('node-schedule');
|
||||||
|
const util = require('./lib/util');
|
||||||
|
|
||||||
// Read internal configuration
|
// Read internal configuration
|
||||||
let config = require('./config.json');
|
let config = require('./config.json');
|
||||||
let twitchChat;
|
let currentPlaylist = config.obs.defaultPlaylist;
|
||||||
|
let twitchChannel = '#' + config.twitch.channels[0].toLowerCase();
|
||||||
const init = (config) => {
|
|
||||||
let botChannel = '#' + config.twitch.username.toLowerCase();
|
|
||||||
|
|
||||||
// Connect to OBS Websocket
|
// Connect to OBS Websocket
|
||||||
const obs = new OBSWebSocket();
|
const obs = new OBSWebSocket();
|
||||||
@@ -23,8 +25,10 @@ const init = (config) => {
|
|||||||
obs.connect({ address: config.obs.websocket.address, password: config.obs.websocket.password })
|
obs.connect({ address: config.obs.websocket.address, password: config.obs.websocket.password })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log(`Success! We're connected to OBS!`);
|
console.log(`Success! We're connected to OBS!`);
|
||||||
//obs.getSourcesList().then(data => {console.log(data.sources)}).catch(console.error);
|
return twitchInit(config, obs);
|
||||||
twitchInit(config, obs);
|
})
|
||||||
|
.then(data => {
|
||||||
|
return streamInit(config, obs, data);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
@@ -35,6 +39,7 @@ const init = (config) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const twitchInit = (config, obs) => {
|
const twitchInit = (config, obs) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
console.log('Connecting to Twitch...');
|
console.log('Connecting to Twitch...');
|
||||||
|
|
||||||
// Connect to Twitch IRC server with the Bot
|
// Connect to Twitch IRC server with the Bot
|
||||||
@@ -67,7 +72,9 @@ const init = (config) => {
|
|||||||
|
|
||||||
// Listen for specific commands from admins
|
// Listen for specific commands from admins
|
||||||
if (config.twitch.admins.includes(from) || from === config.twitch.username.toLowerCase()) {
|
if (config.twitch.admins.includes(from) || from === config.twitch.username.toLowerCase()) {
|
||||||
|
|
||||||
if (commandNoPrefix === 'show' || commandNoPrefix === 'hide') {
|
if (commandNoPrefix === 'show' || commandNoPrefix === 'hide') {
|
||||||
|
|
||||||
let newVisibility = (commandNoPrefix === 'show');
|
let newVisibility = (commandNoPrefix === 'show');
|
||||||
let visibleTerm = (newVisibility ? 'visible' : 'hidden');
|
let visibleTerm = (newVisibility ? 'visible' : 'hidden');
|
||||||
|
|
||||||
@@ -100,32 +107,76 @@ const init = (config) => {
|
|||||||
.catch(err => {
|
.catch(err => {
|
||||||
twitchChat.say(to, JSON.stringify(err));
|
twitchChat.say(to, JSON.stringify(err));
|
||||||
});
|
});
|
||||||
} else if (commandNoPrefix === 'auw') {
|
|
||||||
obs.setSceneItemProperties({"item": "everybody-wow", "visible": true})
|
|
||||||
.then(res => {
|
|
||||||
// fade out headphone audio
|
|
||||||
/*for (i = 100; i >= 0; i--) {
|
|
||||||
obs.setVolume({"source": "headphones", "volume": i/100});
|
|
||||||
}*/
|
|
||||||
// @TODO: send command to mute the songrequest audio
|
|
||||||
editorChat.say(to, '!volume 0');
|
|
||||||
|
|
||||||
twitchChat.say(to, 'Everybody OwenWow');
|
} else if (commandNoPrefix === 't') {
|
||||||
// hide the source after a certain amount of time (248s in this case)
|
let target = commandParts[1] || false;
|
||||||
setTimeout(() => {
|
if (!target) {
|
||||||
obs.setSceneItemProperties({"item": "everybody-wow", "visible": false})
|
twitchChat.say(to, `A scene item name is required!`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sceneItem = {"item": target};
|
||||||
|
|
||||||
|
obs.getSceneItemProperties(sceneItem)
|
||||||
|
.then(data => {
|
||||||
|
let newVisibility = !data.visible;
|
||||||
|
let visibleTerm = (newVisibility ? 'visible' : 'hidden');
|
||||||
|
|
||||||
|
sceneItem.visible = newVisibility;
|
||||||
|
obs.setSceneItemProperties(sceneItem)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
// fade in headphone audio
|
twitchChat.say(to, `${target} is now ${visibleTerm}.`);
|
||||||
/*for (i = 1; i <= 100; i++) {
|
})
|
||||||
obs.setVolume({"source": "headphones", "volume": i/100});
|
.catch(console.error);
|
||||||
}*/
|
})
|
||||||
// @TODO: send command to unmute the songrequest audio
|
.catch(err => {
|
||||||
|
twitchChat.say(to, JSON.stringify(err));
|
||||||
|
});
|
||||||
|
} else if (commandNoPrefix === 'swap') {
|
||||||
|
// hide first argument, show second argument
|
||||||
|
let targetToHide = commandParts[1] || false;
|
||||||
|
let targetToShow = commandParts[2] || false;
|
||||||
|
if (targetToHide === false || targetToShow == false) {
|
||||||
|
twitchChat.say(to, `Format: ${config.twitch.cmdPrefix}swap <item-to-hide> <item-to-show>`);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
obs.setSceneItemProperties({"item": targetToHide, "visible": false})
|
||||||
|
.then(res => {
|
||||||
|
obs.setSceneItemProperties({"item": targetToShow, "visible": true});
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
} else if (commandNoPrefix === 'auw') {
|
||||||
|
// @TODO: switch to 'commercial' scene and show appropriate items, then switch back
|
||||||
|
// this way, playing a commercial doesn't have to know what's playing in the other scene
|
||||||
|
obs.setCurrentScene({"scene-name": "commercial"})
|
||||||
|
.then(res => {
|
||||||
|
// show the video
|
||||||
|
return obs.setSceneItemProperties({"item": "everybody-wow", "scene-name": "commercial", "visible": true});
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
// mute songrequest audio
|
||||||
|
editorChat.say(to, '!volume 0');
|
||||||
|
// show owen
|
||||||
|
obs.setSceneItemProperties({"item": "owen", "scene-name": "commercial", "visible": true});
|
||||||
|
// tell chat what's up
|
||||||
|
twitchChat.say(to, 'Everybody OwenWow');
|
||||||
|
// swap back to fgfm scene after the video ends
|
||||||
|
setTimeout(() => {
|
||||||
|
// hide video
|
||||||
|
obs.setSceneItemProperties({"item": "everybody-wow", "scene-name": "commercial", "visible": false})
|
||||||
|
// hide owen
|
||||||
|
obs.setSceneItemProperties({"item": "owen", "scene-name": "commercial", "visible": false});
|
||||||
|
// unmute songrequest audio
|
||||||
editorChat.say(to, '!volume 75');
|
editorChat.say(to, '!volume 75');
|
||||||
twitchChat.say(to, 'OwenWow');
|
// swap back to fgfm
|
||||||
}).catch(console.error);
|
obs.setCurrentScene({"scene-name": "fgfm"});
|
||||||
}, 248000);
|
}, 248000);
|
||||||
}).catch(console.error);
|
})
|
||||||
|
.catch(console.error);
|
||||||
|
|
||||||
} else if (commandNoPrefix === 'switch') {
|
} else if (commandNoPrefix === 'switch') {
|
||||||
|
|
||||||
let target = commandParts[1] || false;
|
let target = commandParts[1] || false;
|
||||||
if (!target) {
|
if (!target) {
|
||||||
twitchChat.say(to, `A scene name is required!`);
|
twitchChat.say(to, `A scene name is required!`);
|
||||||
@@ -182,10 +233,123 @@ const init = (config) => {
|
|||||||
twitchChat.addListener('motd', motd => {
|
twitchChat.addListener('motd', motd => {
|
||||||
console.log(`Received MOTD: ${motd}`);
|
console.log(`Received MOTD: ${motd}`);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
resolve({"botChat": twitchChat, "editorChat": editorChat});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
init(config);
|
const streamInit = (config, obs, twitch) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
console.log(`Initializing stream timers...`);
|
||||||
|
|
||||||
|
// When: Hourly at 55 past
|
||||||
|
// What: AUW
|
||||||
|
let auwJob = schedule.scheduleJob({minute: 55}, (fireDate) => {
|
||||||
|
// AUW
|
||||||
|
twitch.editorChat.say(twitchChannel, `${config.twitch.cmdPrefix}auw`);
|
||||||
|
});
|
||||||
|
console.log(`AUW is scheduled to be shown at ${auwJob.nextInvocation()}`);
|
||||||
|
|
||||||
|
let userVotes = [];
|
||||||
|
let playlistChoices = config.obs.availablePlaylists.map((e, i, a) => {
|
||||||
|
return `[${i+1}] ${e.chatName}`;
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
twitch.botChat.say(twitchChannel, `Vote for which video playlist you'd like to see next using ${config.twitch.cmdPrefix}vote #: ${playlistChoices.join(' | ')}`);
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
// When: Every 2 Hours
|
||||||
|
// What: Change the video playlist
|
||||||
|
let changePlaylistJob = schedule.scheduleJob("*/5 * * * *", () => {
|
||||||
|
// Base the selection on user votes collected since the last invocation (unless there are 0 votes, then choose randomly)
|
||||||
|
let newPlaylist;
|
||||||
|
if (userVotes.length === 0) {
|
||||||
|
// choose a random item other than currentPlaylist from config.obs.availablePlaylists
|
||||||
|
let choices = config.obs.availablePlaylists.slice(0);
|
||||||
|
currentChoice = choices.indexOf(e => e.sceneItem === currentPlaylist);
|
||||||
|
choices.splice(currentChoice, 1);
|
||||||
|
newPlaylist = util.randElement(choices);
|
||||||
|
console.log(`PLAYLIST CHOSEN RANDOMLY: ${newPlaylist.chatName}`);
|
||||||
|
} else {
|
||||||
|
// tally and sort votes
|
||||||
|
let tallied = userVotes.reduce((voteTallies, currentValue) => {
|
||||||
|
tallyIndex = voteTallies.find(e.id === currentValue.vote);
|
||||||
|
if (tallyIndex !== -1) {
|
||||||
|
voteTallies[tallyIndex].count++;
|
||||||
|
} else {
|
||||||
|
voteTallies.push({id: currentValue.vote, count: 1});
|
||||||
|
}
|
||||||
|
}).sort((a, b) => {
|
||||||
|
if (a.count < b.count) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.count > b.count) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// a must be equal to b
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`[TEST] Voting Results: ${JSON.stringify(tallied)}`);
|
||||||
|
newPlaylist = config.obs.availablePlaylists[tallied[0].id-1];
|
||||||
|
console.log(`WINNER OF THE VOTE: ${newPlaylist.chatName}`);
|
||||||
|
//twitch.botChat.say(twitchChannel, `[TEST] Voting Results: ${JSON.stringify(tallied)}`);
|
||||||
|
|
||||||
|
// clear user votes
|
||||||
|
userVotes = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*twitch.botChat.say(twitchChannel, `[TEST] Changing playlist from ${currentPlaylist} to ${newPlaylist.chatName}`);
|
||||||
|
twitch.editorChat.say(twitchChannel, `[TEST] ${config.twitch.cmdPrefix}swap ${currentPlaylist} ${newPlaylist.sceneItem}`);
|
||||||
|
twitch.editorChat.say(twitchChannel, `[TEST] !setcurrent NOW SHOWING: ${newPlaylist.activity}`);*/
|
||||||
|
console.log(`Changing playlist from ${currentPlaylist} to ${newPlaylist.chatName}`);
|
||||||
|
console.log(`${config.twitch.cmdPrefix}swap ${currentPlaylist} ${newPlaylist.sceneItem}`);
|
||||||
|
console.log(`!setcurrent NOW SHOWING: ${newPlaylist.activity}`);
|
||||||
|
currentPlaylist = newPlaylist.sceneItem;
|
||||||
|
});
|
||||||
|
console.log(`Playlist will be changed at ${changePlaylistJob.nextInvocation()}`);
|
||||||
|
|
||||||
|
// Track user votes for playlist
|
||||||
|
twitch.botChat.addListener('message', (from, to, message) => {
|
||||||
|
// Ignore everything from blacklisted users
|
||||||
|
if (config.twitch.blacklistedUsers.includes(from)) return;
|
||||||
|
|
||||||
|
// Listen for commands that start with the designated prefix
|
||||||
|
if (message.startsWith(config.twitch.cmdPrefix)) {
|
||||||
|
let commandParts = message.slice(config.twitch.cmdPrefix.length).split(' ');
|
||||||
|
let commandNoPrefix = commandParts[0] || '';
|
||||||
|
if (commandNoPrefix === 'vote') {
|
||||||
|
let userVote = commandParts[1] || false;
|
||||||
|
|
||||||
|
if (userVote === false) {
|
||||||
|
return twitch.botChat.say(to, `Vote for which video playlist you'd like to see next using ${config.twitch.cmdPrefix}vote #: ${playlistChoices.join(' | ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
userVote = Number.parseInt(userVote);
|
||||||
|
|
||||||
|
if (!Number.isInteger(userVote) || userVote < 1 || userVote > playlistChoices.length) {
|
||||||
|
return twitch.botChat.say(to, `@${from}, please choose an option from 1 - ${playlistChoices.length}!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for uniqueness of vote
|
||||||
|
// if it's not unique, update the vote
|
||||||
|
let prevVote = userVotes.findIndex(e => e.from === from);
|
||||||
|
if (prevVote !== -1) {
|
||||||
|
if (userVotes[prevVote].vote !== userVote) {
|
||||||
|
// update vote and inform the user
|
||||||
|
userVotes[prevVote].vote = userVote;
|
||||||
|
twitch.botChat.say(to, `@${from}, your vote has been updated!`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// log user vote
|
||||||
|
userVotes.push({"from": from, "vote": userVote});
|
||||||
|
twitch.botChat.say(to, `@${from}, your vote has been registered!`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// catches Promise errors
|
// catches Promise errors
|
||||||
process.on('unhandledRejection', console.error);
|
process.on('unhandledRejection', console.error);
|
||||||
|
|||||||
Reference in New Issue
Block a user