From 232971098bf0fc3845d09061ae9cc13245d845e1 Mon Sep 17 00:00:00 2001 From: choelzl Date: Sat, 5 Feb 2022 17:45:49 +0100 Subject: [PATCH] Fixing overlay --- web/js/sockerWrapper.js | 625 ++++++++++++++++++ web/overlay/index.html | 44 ++ web/overlay/index.js | 158 +++++ web/overlay/{follow/index.html => styles.css} | 0 web/overlay/subscribe/index.html | 0 5 files changed, 827 insertions(+) create mode 100644 web/js/sockerWrapper.js create mode 100644 web/overlay/index.html create mode 100644 web/overlay/index.js rename web/overlay/{follow/index.html => styles.css} (100%) delete mode 100644 web/overlay/subscribe/index.html diff --git a/web/js/sockerWrapper.js b/web/js/sockerWrapper.js new file mode 100644 index 0000000..fa6c78d --- /dev/null +++ b/web/js/sockerWrapper.js @@ -0,0 +1,625 @@ +/* + * Copyright (C) 2016-2019 phantombot.tv + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//NOTE: this is straight copied from panel's index.js +// Main socket and functions. +$(function() { + + + var helpers = {}; + + helpers.DEBUG_STATES = { + NONE: 0, + DEBUG: 1, + INFO: 2, + FORCE: 3 + }; + // Debug status. 0 = off | 1 = on. + helpers.DEBUG_STATE = (localStorage.getItem('phantombot_debug_state') !== null ? parseInt(localStorage.getItem('phantombot_debug_state')) : helpers.DEBUG_STATES.NONE); + // Debug types. + helpers.LOG_TYPE = helpers.DEBUG_STATES; + + /* + * @function Used to print debug messages in the console. + * + * @param {String} message + * @param {Number} type + */ + helpers.log = function(message, type) { + if (helpers.DEBUG_STATE === helpers.DEBUG_STATES.DEBUG || type === helpers.DEBUG_STATE || type === helpers.LOG_TYPE.FORCE) { + console.log('%c[PhantomBot Log]', 'color: #6441a5; font-weight: 900;', message); + } + }; + + /* + * @function Used to print error messages in the console. + * + * @param {String} message + * @param {Number} type + */ + helpers.logError = function(message, type) { + console.log('%c[PhantomBot Error]', 'color: red; font-weight: 900;', message); + }; + + + + + var webSocket = new ReconnectingWebSocket((getProtocol() === 'https://' || window.location.protocol === 'https:' ? 'wss://' : 'ws://') + window.location.host + '/ws/panel', null, { reconnectInterval: 500 }), + callbacks = [], + listeners = [], + familyHandlers = {}, + socket = {}; + + /* + * @function Used to send messages to the socket. This should be private to this script. + * + * @param {Object} message + */ + var sendToSocket = function(message) { + try { + let json = JSON.stringify(message); + + webSocket.send(json); + + // Make sure to not show the user's token. + if (json.indexOf('authenticate') !== -1) { + helpers.log('sendToSocket:: ' + json.substring(0, json.length - 20) + '.."}', helpers.LOG_TYPE.DEBUG); + } else { + helpers.log('sendToSocket:: ' + json, helpers.LOG_TYPE.DEBUG); + } + } catch (e) { + helpers.logError('Failed to send message to socket: ' + e.message, helpers.LOG_TYPE.DEBUG); + } + }; + + /* + * @function Generates a callback + * + * @param {String} id + * @param {Array} tables + * @param {Boolean} isUpdate + * @param {Function} callback + * @param {Boolean} storeKey + */ + var generateCallBack = function(id, tables, isUpdate, isArray, callback, storeKey) { + if (callbacks[id] !== undefined) { + helpers.logError('Callback with id "' + id + '" exists already. Aborting update.', helpers.LOG_TYPE.FORCE); + } else { + helpers.log('Created callback with id ' + id, helpers.LOG_TYPE.DEBUG); + + callbacks[id] = { + await: (tables.length === 0 ? 1 : tables.length), + isUpdate: isUpdate, + isArray: isArray, + func: function(e) { + try { + callback(e); + } catch (ex) { + // Line number won't be accurate, function will by anonymous, but we get the stack so it should be fine. + helpers.logError('Failed to run callback: ' + ex.stack, helpers.LOG_TYPE.FORCE); + } + }, + storeKey: storeKey, + queryData: [] + }; + } + }; + + /* + * @function Adds a listener for the socket. + * + * @param {String} listener_id + * @param {Function} callback + */ + socket.addListener = function(listener_id, callback) { + if (listeners[listener_id] === undefined) { + helpers.log('Adding listener with id ' + listener_id); + listeners[listener_id] = function(e) { + try { + callback(e); + } catch (ex) { + // Line number won't be accurate, function will by anonymous, but we get the stack so it should be fine. + helpers.logError('Failed to run listener: ' + ex.stack, helpers.LOG_TYPE.FORCE); + } + }; + } + }; + + /* + * @function Removes a listener from the socket. + * + * @param {String} listener_id + */ + socket.removeListener = function(listener_id) { + if (listeners[listener_id] !== undefined) { + delete listeners[listener_id]; + } + }; + + socket.addFamilyHandler = function(familyName, callback) { + familyHandlers[familyName] = function(e) { + try { + callback(e); + } catch (ex) { + // Line number won't be accurate, function will by anonymous, but we get the stack so it should be fine. + helpers.logError('Failed to run family handler: ' + ex.stack, helpers.LOG_TYPE.FORCE); + } + }; + } + + socket.removeFamilyHandler = function(familyName) { + if (familyHandlers[familyName] !== undefined) { + delete listeners[familyName]; + } + }; + + /* + * @function Runs a bot commands as the bot in async, thus returning right away. + * + * @param {String} callback_id + * @param {String} command + * @param {Function} callback + */ + socket.sendCommand = function(callback_id, command, callback) { + // Genetate a callback. + generateCallBack(callback_id, [], true, false, callback); + + // Send the command. + sendToSocket({ + command: String(command), + query_id: callback_id + }); + }; + + /* + * @function Sends a raw request to the socket. + * + * @param {String} callback_id + * @param {Function} callback + */ + socket.getBotVersion = function(callback_id, callback) { + // Genetate a callback. + generateCallBack(callback_id, [], true, false, callback); + + // Send the request. + sendToSocket({ + version: callback_id + }); + }; + + /* + * @function Runs a bot commands as the bot. + * + * @param {String} callback_id + * @param {String} command + * @param {Function} callback + */ + socket.sendCommandSync = function(callback_id, command, callback) { + // Genetate a callback. + generateCallBack(callback_id, [], true, false, callback); + + // Send the command. + sendToSocket({ + command_sync: String(command), + query_id: callback_id + }); + }; + + /* + * @function Sends the websocket event. + * + * @param {String} callback_id + * @param {String} script + * @param {String} argsString + * @param {Array} args + * @param {Function} callback + */ + socket.wsEvent = function(callback_id, script, argsString, args, callback) { + // Genetate a callback. + generateCallBack(callback_id, [], true, false, callback); + + // Send event. + sendToSocket({ + socket_event: callback_id, + script: script, + args: { + arguments: String(argsString), + args: args + } + }); + }; + + /* + * @function Updates a value in the database of the bot. + * + * @param {String} callback_id + * @param {String} table + * @param {String} key + * @param {String} value + * @param {Function} callback + */ + socket.updateDBValue = function(callback_id, table, key, value, callback) { + // Genetate a callback. + generateCallBack(callback_id, [], true, false, callback); + + // Update the value. + sendToSocket({ + dbupdate: callback_id, + update: { + table: String(table), + key: String(key), + value: String(value) + } + }); + }; + + /* + * @function Updates values in the database of the bot. + * + * @param {String} callback_id + * @param {Object} dataObj {tables: [], keys: [], values: } + * @param {Function} callback + */ + socket.updateDBValues = function(callback_id, dataObj, callback) { + // Genetate a callback. + generateCallBack(callback_id, dataObj.tables, true, false, callback); + + // Start sending the updates to the socket. + for (let i = 0; i < dataObj.tables.length; i++) { + sendToSocket({ + dbupdate: callback_id, + update: { + table: String(dataObj.tables[i]), + key: String(dataObj.keys[i]), + value: String(dataObj.values[i]) + } + }); + } + }; + + /* + * @function Increases a value in the database. + * + * @param {String} callback_id + * @param {String} table + * @param {String} key + * @param {String} value + * @param {Function} callback + */ + socket.incrDBValue = function(callback_id, table, key, value, callback) { + // Genetate a callback. + generateCallBack(callback_id, [], true, false, callback); + + // Update the value. + sendToSocket({ + dbincr: callback_id, + incr: { + table: table, + key: key, + value: value + } + }); + }; + + /* + * @function Decreases a value in the database. + * + * @param {String} callback_id + * @param {String} table + * @param {String} key + * @param {String} value + * @param {Function} callback + */ + socket.decrDBValue = function(callback_id, table, key, value, callback) { + // Genetate a callback. + generateCallBack(callback_id, [], true, false, callback); + + // Update the value. + sendToSocket({ + dbdecr: callback_id, + decr: { + table: table, + key: key, + value: value + } + }); + }; + + /* + * @function Gets a value from the database + * + * @param {String} callback_id + * @param {String} table + * @param {String} key + * @param {Function} callback + */ + socket.getDBValue = function(callback_id, table, key, callback) { + // Genetate a callback. + generateCallBack(callback_id, [], false, false, callback); + + // Query database. + sendToSocket({ + dbquery: callback_id, + query: { + table: String(table), + key: String(key) + } + }); + }; + + /* + * @function Gets values from the database + * + * @param {String} callback_id + * @param {Object} dataObj {tables: [], keys: []} + * @param {Function} callback + * @param {Boolean} storeKey - Store the value with the key name from the DB. Default stores it as the table, thus making it only possible to query the table once. + */ + socket.getDBValues = function(callback_id, dataObj, storeKey, callback) { + callback = (callback === undefined ? storeKey : callback); + + // Genetate a callback. + generateCallBack(callback_id, dataObj.tables, false, false, callback, (typeof storeKey === 'function' ? false : true)); + + // Start sending the updates to the socket. + for (let i = 0; i < dataObj.tables.length; i++) { + sendToSocket({ + dbquery: callback_id, + query: { + table: String(dataObj.tables[i]), + key: String(dataObj.keys[i]) + } + }); + } + }; + + /* + * @function Gets values from the database by an order. + * + * @param {String} callback_id + * @param {String} table + * @param {Number} limit + * @param {Number} offset + * @param {String} order + * @param {Function} callback + */ + socket.getDBTableValuesByOrder = function(callback_id, table, limit, offset, order, isNumber, callback) { + // Genetate a callback. + generateCallBack(callback_id, [], false, true, callback); + + // Query database. + sendToSocket({ + dbvaluesbyorder: callback_id, + query: { + table: table, + limit: String(limit), + offset: String(offset), + order: order, + number: String(isNumber) + } + }); + }; + + /* + * @function Gets all keys and values from a database table. + * + * @param {String} callback_id + * @param {String} table + * @param {Function} callback + */ + socket.getDBTableValues = function(callback_id, table, callback) { + // Genetate a callback. + generateCallBack(callback_id, [], false, true, callback); + + // Query database. + sendToSocket({ + dbkeys: callback_id, + query: { + table: String(table) + } + }); + }; + + /* + * @function Gets all keys and values from multiple database table. + * + * @param {String} callback_id + * @param {Array Object} tables [{table: 'a'}, {table: 'b'}] + * @param {Function} callback + */ + socket.getDBTablesValues = function(callback_id, tables, callback) { + // Genetate a callback. + generateCallBack(callback_id, [], false, true, callback); + + // Query database. + sendToSocket({ + dbkeyslist: callback_id, + query: tables + }); + }; + + /* + * @function Removes the data from the database. + * + * @param {String} callback_id + * @param {String} table + * @param {String} key + * @param {Function} callback + */ + socket.removeDBValue = function(callback_id, table, key, callback) { + // Genetate a callback. + generateCallBack(callback_id, [], false, true, callback); + + // Send the event. + sendToSocket({ + dbdelkey: callback_id, + delkey: { + table: String(table), + key: String(key) + } + }); + }; + + /* + * @function Removes the data from the database. + * + * @param {String} callback_id + * @param {Object} dataObj {tables: [], keys: []} + * @param {Function} callback + */ + socket.removeDBValues = function(callback_id, dataObj, callback) { + // Genetate a callback. + generateCallBack(callback_id, dataObj.tables, false, true, callback); + + for (let i = 0; i < dataObj.tables.length; i++) { + // Send the event. + sendToSocket({ + dbdelkey: callback_id, + delkey: { + table: String(dataObj.tables[i]), + key: String(dataObj.keys[i]) + } + }); + } + }; + + // WebSocket events. + + socket.getReadyState = function() {return webSocket.readyState;} + + /* + * @function Called when the socket opens. + */ + webSocket.onopen = function() { + helpers.log('Connection established with the websocket.', helpers.LOG_TYPE.FORCE); + // Auth with the socket. + sendToSocket({ + authenticate: getAuth() + }); + }; + + /* + * @function Socket calls when it closes + */ + webSocket.onclose = function() { + helpers.logError('Connection lost with the websocket.', helpers.LOG_TYPE.FORCE); + }; + + /* + * @function Socket calls when it gets message. + */ + webSocket.onmessage = function(e) { + try { + helpers.log('Message from socket: ' + e.data, helpers.LOG_TYPE.DEBUG); + + if (e.data === 'PING') { + webSocket.send('PONG'); + return; + } + + let message = JSON.parse(e.data); + + // Check this message here before doing anything else. + if (message.authresult !== undefined) { + if (message.authresult === 'false') { + helpers.logError('Failed to auth with the socket.', helpers.LOG_TYPE.FORCE); + } else { + // This is to stop a reconnect loading the main page. + if (helpers.isAuth === true) { + return; + } else { + helpers.isAuth = true; + } + + // XXX: revent loading main page + //$.loadPage('dashboard', 'dashboard.html'); + } + return; + } + + // Make sure this isn't a version request. + if (message.versionresult !== undefined) { + // Call the callback. + callbacks[message.versionresult].func(message); + // Delete the callback. + delete callbacks[message.versionresult]; + } else if(message.query_id !== undefined) { + + // console.log("base got websocket with data"); + // console.log(e); + + // Handle callbacks. + let callback = callbacks[message.query_id], + listener = listeners[message.query_id]; + + if (callback !== undefined) { + // Add our data to the callback array. + if (!callback.isUpdate) { + if (callback.isArray) { + callback.queryData = message.results; + } else if (callback.storeKey === true) { + callback.queryData[Object.keys(message.results)[1]] = message.results.value; + } else { + callback.queryData[message.results.table] = message.results.value; + } + } + + // If we got all the data, run the callback. + if (--callback.await === 0) { + // Run the function and send the query data with it. + callback.func(callback.queryData); + // Log this. + helpers.log('Called callback with id: ' + message.query_id, helpers.LOG_TYPE.DEBUG); + // Remove the callback from the array. + delete callbacks[message.query_id]; + + // Remove any active spinners. + if (message.query_id !== 'get_bot_updates' && message.query_id.indexOf('get') !== -1) { + // Remove any active spinners. + $('.load-ajax').remove(); + } + + if (message.query_id.indexOf('module_toggle') !== -1 || message.query_id.indexOf('module_status') !== -1 + || message.query_id.endsWith('module')) { + if (message.results.value == 'false') { + $('.load-ajax').remove(); + } + } + } else { + helpers.log('Awaiting for data (' + callback.await + ' left) before calling callback with id: ' + message.query_id, helpers.LOG_TYPE.DEBUG); + } + } else if (listener !== undefined) { + // Call the listener. + listener(message.results); + } + } else if(message.eventFamily !== undefined) { + let handler = familyHandlers[message.eventFamily]; + if(handler !== undefined) { + handler(e) + } + } + } catch (ex) { + // Line number won't be accurate, function will by anonymous, but we get the stack so it should be fine. + helpers.logError('Failed to parse message from socket: ' + ex.stack + '\n\n' + e.data, helpers.LOG_TYPE.FORCE); + } + }; + + // Make this a global object. + window.socket = socket; + // Store all timers in here so we can destroy them. + window.timers = []; +}); \ No newline at end of file diff --git a/web/overlay/index.html b/web/overlay/index.html new file mode 100644 index 0000000..c5a2a14 --- /dev/null +++ b/web/overlay/index.html @@ -0,0 +1,44 @@ + + + + + + PlampBot Song Requests + + + + + + + + + +
+ +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/overlay/index.js b/web/overlay/index.js new file mode 100644 index 0000000..22e2b57 --- /dev/null +++ b/web/overlay/index.js @@ -0,0 +1,158 @@ +$(function () { + var webSocket = getWebSocket(), + queryMap = getQueryMap(), + isDebug = localStorage.getItem('phantombot_follow_debug') === 'true' || false; + queue = []; + + /* + * @function Gets a new instance of the websocket. + * + * @return {ReconnectingWebSocket} + */ + function getWebSocket() { + let socketUri = ((window.location.protocol === 'https:' ? 'wss://' : 'ws://') + window.location.host + '/ws/followpolls'), // URI of the socket. + reconnectInterval = 5000; // How often in milliseconds we should try reconnecting. + + return new ReconnectingWebSocket(socketUri, null, { + reconnectInterval: reconnectInterval + }); + } + + /* + * @function Parses the query params in the URL and puts them into a map. + * + * @return {Map} + */ + function getQueryMap() { + let queryString = window.location.search, // Query string that starts with ? + queryParts = queryString.substr(1).split('&'), // Split at each &, which is a new query. + queryMap = new Map(); // Create a new map for save our keys and values. + + for (let i = 0; i < queryParts.length; i++) { + let key = queryParts[i].substr(0, queryParts[i].indexOf('=')), + value = queryParts[i].substr(queryParts[i].indexOf('=') + 1, queryParts[i].length); + + if (key.length > 0 && value.length > 0) { + queryMap.set(key, value); + } + } + + return queryMap; + } + + /* + * @function Prints debug logs. + * + * @param {String} message + */ + function printDebug(message, force) { + if (isDebug || force) { + console.log('%c[PhantomBot Log]', 'color: #6441a5; font-weight: 900;', message); + } + } + + /* + * @function Toggles the debug mode. + * + * @param {String} toggle + */ + window.toggleDebug = function (toggle) { + localStorage.setItem('phantombot_follow_debug', toggle.toString()); + + // Refresh the page. + window.location.reload(); + } + + /* + * @function Checks if the query map has the option, if not, returns default. + * + * @param {String} option + * @param {String} def + * @return {String} + */ + function getOptionSetting(option, def) { + if (queryMap.has(option)) { + return queryMap.get(option); + } else { + return def; + } + } + + /* + * @function Sends a message to the socket + * + * @param {String} message + */ + function sendToSocket(message) { + try { + webSocket.send(JSON.stringify(message)); + } catch (ex) { + printDebug('Failed to send a message to the socket: ' + ex.stack); + } + } + + + function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + /* + * @event Called once the socket opens. + */ + webSocket.onopen = function () { + printDebug('Successfully connected to the socket.', true); + // Authenticate with the socket. + sendToSocket({ + authenticate: getAuth() + }); + }; + + /* + * @event Called when the socket closes. + */ + webSocket.onclose = function () { + printDebug('Disconnected from the socket.', true); + }; + + /* + * @event Called when we get a message. + * + * @param {Object} e + */ + webSocket.onmessage = function (e) { + try { + let rawMessage = e.data, + message = JSON.parse(rawMessage); + + printDebug('[MESSAGE] ' + rawMessage); + + if (message.query_id === undefined) { + // Check for our auth result. + if (message.authresult !== undefined) { + if (message.authresult === 'true') { + printDebug('Successfully authenticated with the socket.', true); + // Handle this. + handleBrowserInteraction() + } else { + printDebug('Failed to authenticate with the socket.', true); + } + } else + + // Queue all events and process them one at-a-time. + if (message.alert_image !== undefined || message.audio_panel_hook !== undefined) { + queue.push(message); + } + + // Message cannot be handled error. + else { + printDebug('Failed to process message from socket: ' + rawMessage); + } + } + } catch (ex) { + printDebug('Failed to parse socket message [' + e.data + ']: ' + e.stack); + } + }; + + // Handle processing the queue. + setInterval(handleQueue, 5e2); +}); \ No newline at end of file diff --git a/web/overlay/follow/index.html b/web/overlay/styles.css similarity index 100% rename from web/overlay/follow/index.html rename to web/overlay/styles.css diff --git a/web/overlay/subscribe/index.html b/web/overlay/subscribe/index.html deleted file mode 100644 index e69de29..0000000