From 2689708b49c40d601ab576bf132eebcbfe346539 Mon Sep 17 00:00:00 2001 From: Jiiks Date: Fri, 6 May 2016 21:34:40 +0300 Subject: [PATCH] 0.2.8 Mostly error catching, require *.plugin.js *.theme.css, better hooking --- lib/BetterDiscord.js | 869 ++++++++++++++++++++++++------------------- lib/Utils.js | 31 +- lib/config.json | 2 +- 3 files changed, 511 insertions(+), 391 deletions(-) diff --git a/lib/BetterDiscord.js b/lib/BetterDiscord.js index 2d84b46f..10a3cc86 100644 --- a/lib/BetterDiscord.js +++ b/lib/BetterDiscord.js @@ -1,315 +1,153 @@ /* BetterDiscordApp Entry - * Version: 2.2 + * Version: 3.0 * Author: Jiiks | http://jiiks.net * Date: 27/08/2015 - 15:51 - * Last Update: 30/03/2016 + * Last Update: 06/05/2016 * https://github.com/Jiiks/BetterDiscordApp */ -//Imports +'use strict'; + var _fs = require("fs"); var _config = require("./config.json"); var _utils = require("./utils"); -var _ipc = require('ipc'); +var _utils2; +var _bdIpc = require('electron').ipcMain; +var _error = false; -var _repo = "Jiiks"; +var _eol = require('os').EOL; -//Beta flag -var _beta = false; -//Local flag -var _local = false; -var _localServer = "http://localhost"; - -//Variables -var _version; var _mainWindow; -var _updater; -var _hash; -var _userDefault = { "cache": null }; -var _userConfig = _userDefault; -var _cacheExpired = true; -var _cacheDays = 0; +var _cfg = {}; +var _extData = {}; -var _dataPath; - -//IDE -/*_config = { - "Core": { - "Version": "0.2.7" - } -};*/ - -//noinspection JSUnresolvedVariable; -var _os = process.platform; -var _userFile; - -var _this; function BetterDiscord(mainWindow) { - _this = this; _mainWindow = mainWindow; - _version = _config.Core.Version; - _utils = new _utils.Utils(mainWindow); - - this.createAndCheckData(this.init); + _cfg = _config.cfg; + _cfg.version = _config.Core.Version; + _cfg.os = process.platform; + _utils2 = new _utils.Utils(mainWindow); + hook(); + createAndCheckData(); } -BetterDiscord.prototype.initLoaders = function(){ - var os = process.platform; - var _dataPath = os == "win32" ? process.env.APPDATA : os == 'darwin' ? process.env.HOME + '/Library/Preferences' : '/var/local'; - _dataPath += "/BetterDiscord/"; +function createAndCheckData() { + getUtils().log("Checking data/cache"); - if (!_fs.existsSync(_dataPath)) { - _this.getUtils().log('BetterDiscord: Creating BD Dir'); - _fs.mkdirSync(_dataPath); - } - - if (!_fs.existsSync(_dataPath + "plugins/")) { - _this.getUtils().log('BetterDiscord: Creating Plugins Dir'); - _fs.mkdirSync(_dataPath + "plugins/"); - } - - if (!_fs.existsSync(_dataPath + "themes/")) { - _this.getUtils().log('BetterDiscord: Creating Themes Dir'); - _fs.mkdirSync(_dataPath + "themes/"); - } - _mainWindow.webContents.executeJavaScript('var themesupport2 = true'); - - _fs.readdir(_dataPath + "plugins/", function(err, files) { - if (err) { - _this.getUtils().log(err); - return; - } - _mainWindow.webContents.executeJavaScript('var bdplugins = {};'); - files.forEach(function(fileName) { - var plugin = _fs.readFileSync(_dataPath + "plugins/" + fileName, 'utf8'); - var meta = plugin.split('\n')[0]; - if (meta.indexOf('META') < 0) { - _this.getUtils().log('BetterDiscord: ERROR[Plugin META not found in file: ' + fileName + ']'); - return; - } - var pluginVar = meta.substring(meta.lastIndexOf('//META') + 6, meta.lastIndexOf('*//')); - var parse = JSON.parse(pluginVar); - var pluginName = parse['name']; - _this.getUtils().log('BetterDiscord: Loading Plugin: ' + pluginName); - _mainWindow.webContents.executeJavaScript(plugin); - _mainWindow.webContents.executeJavaScript('(function() { var plugin = new ' + pluginName + '(); bdplugins[plugin.getName()] = { "plugin": plugin, "enabled": false } })();') - }); - }); - - _fs.readdir(_dataPath + 'themes/', function(err, files) { - if (err) { - _this.getUtils().log(err); - return; - } - _mainWindow.webContents.executeJavaScript('var bdthemes = {};'); - files.forEach(function(fileName) { - var theme = _fs.readFileSync(_dataPath + 'themes/' + fileName, 'utf8'); - var split = theme.split('\n'); - var meta = split[0]; - if (meta.indexOf('META') < 0) { - _this.getUtils().log('BetterDiscord: ERROR[Theme META not found in file: ' + fileName + ']'); - return; - } - var themeVar = meta.substring(meta.lastIndexOf('//META') + 6, meta.lastIndexOf('*//')); - var parse = JSON.parse(themeVar); - var themeName = parse['name']; - var themeAuthor = parse['author']; - var themeDescription = parse['description']; - var themeVersion = parse['version']; - _this.getUtils().log('BetterDiscord: Loading Theme: ' + themeName); - split.splice(0, 1); - theme = split.join('\n'); - theme = theme.replace(/(\r\n|\n|\r)/gm, ''); - _mainWindow.webContents.executeJavaScript('(function() { bdthemes["' + themeName + '"] = { "enabled": false, "name": "' + themeName + '", "css": "' + escape(theme) + '", "description": "' + themeDescription + '", "author":"' + themeAuthor + '", "version":"' + themeVersion + '" } })();'); - }); - }); -}; - -BetterDiscord.prototype.getUtils = function() { - return _utils; -}; - -BetterDiscord.prototype.createAndCheckData = function(callback) { - - this.getUtils().log("Checking Cache"); - - //New data path - //noinspection JSUnresolvedVariable - _dataPath = _os == "win32" ? process.env.APPDATA : _os == 'darwin' ? process.env.HOME + '/Library/Preferences' : '/var/local'; - _dataPath += "/BetterDiscord"; - _userFile = _dataPath + "/user.json"; + _cfg.dataPath = (_cfg.os == 'win32' ? process.env.APPDATA : _cfg.os == 'darwin' ? process.env.HOME + '/Library/Preferences' : '/var/local') + '/BetterDiscord/'; + _cfg.userFile = _cfg.dataPath + 'user.json'; try { - //Create data path folder if it doesn't exist - if (!_fs.existsSync(_dataPath)) { - this.getUtils().log("Creating Data Folder @" + _dataPath); - _fs.mkdirSync(_dataPath); + getUtils().mkdirSync(_cfg.dataPath); + + if(_fs.existsSync(_cfg.userFile)) { + _cfg.userCfg = JSON.parse(_fs.readFileSync(_cfg.userFile)); } - //Read user config if it exists - if(_fs.existsSync(_userFile)) { - _userConfig = JSON.parse(_fs.readFileSync(_userFile)); - } - - //Userfile doesn't exist - if(_userConfig.cache == null) { - _userConfig.cache = new Date(); + if(_cfg.userCfg.cache == null) { + _cfg.userCfg.cache = new Date(); } else { var currentDate = new Date(); - var cacheDate = new Date(_userConfig.cache); + var cacheDate = new Date(_cfg.userCfg.cache); //Check if cache is expired - if(Math.abs(currentDate.getDate() - cacheDate.getDate()) > _cacheDays) { - _userConfig.cache = currentDate; + if(Math.abs(currentDate.getDate() - cacheDate.getDate()) > _cfg.cache.days) { + _cfg.userCfg.cache = currentDate; } else { - _cacheExpired = false; + _cfg.cache.expired = false; } } //Write new cache date if expired - if(_cacheExpired) { - this.getUtils().log("Cache Expired or NULL"); - _fs.writeFileSync(_userFile, JSON.stringify(_userConfig)); + if(_cfg.cache.expired) { + getUtils().log("Cache expired or null"); + _fs.writeFileSync(_cfg.userFile, JSON.stringify(_cfg.userCfg)); } - callback(); - }catch(err) { - //Exit BD + init(); + } catch(err) { + getUtils().err(err); + exit(err.message); } -}; +} -BetterDiscord.prototype.init = function() { - var self = this; +function init() { + if(_cfg.branch == null) { + _cfg.branch = _cfg.beta ? "beta" : "master"; + } - this.getUtils().log("Initializing"); + if(_cfg.repo == null) { + _cfg.repo = "Jiiks"; + } - this.getUtils().log("Getting latest hash"); - - var branch = _beta ? "beta" : "master"; - - this.getUtils().log("Using repo: " + _repo + " and branch: " + branch); - - //Get the latest commit hash - this.getUtils().download("api.github.com", "/repos/" + _repo + "/BetterDiscordApp/commits/" + branch, function(data) { - - try { - _hash = JSON.parse(data).sha; - self.getUtils().execJs("var _hash = " + _hash + ";"); - }catch(err) { - self.quit("Failed to load hash"); - return; - } - - if(typeof(_hash) == "undefined") { - self.quit("Failed to load hash"); - return; - } - - self.getUtils().log("Hash: "+ _hash); - - //Download latest updater - self.getUtils().download("raw.githubusercontent.com", "/" + _repo + "/BetterDiscordApp/" + _hash + "/data/updater.json", function(data) { - - try { - _updater = JSON.parse(data); - }catch(err) { - self.quit("Failed to load updater data"); - return; - } - - if(typeof(_updater) == "undefined") { - self.quit("Failed to load updater data"); - return; - } - - self.getUtils().log("Latest Versions: " + _updater.LatestVersion); - self.getUtils().log("Using CDN: " + _updater.CDN); - self.getUtils().log("Starting up"); - self.updateData(); - self.start(); - }); + getUtils().log("Using repository: " + _cfg.repo + " and branch: " + _cfg.branch); + getUtils().log("Getting latest hash"); + getUtils().attempt(getHash, 3, 0, "Failed to load hash", initUpdater, function() { + exit("Failed to load hash after 3 attempts"); }); -}; +} -BetterDiscord.prototype.start = function() { - _this.getUtils().log("Hooking dom-ready"); - var webContents = _this.getUtils().getWebContents(); - webContents.on('dom-ready', function() { _this.domReady(); }); - webContents.on("did-finish-loading", function() { if(!_domHooked) { _this.getUtils().log("Failsafe"); _this.domReady(); } }); -}; - -BetterDiscord.prototype.quit = function(reason) { - _this.getUtils().log("BetterDiscord ERR: " + reason); -}; - -var ipcHooked = false; - -var _domHooked = false; - -BetterDiscord.prototype.domReady = function() { - - _domHooked = true; - - if(ipcHooked) { - _this.load(true); - return; - } - - ipcHooked = true; - _this.load(false); -}; - -BetterDiscord.prototype.load = function(reload) { - _this.getUtils().log("Hooked dom-ready"); - - _this.initLoaders(); - - if(reload) { - _this.getUtils().log("Reloading"); - } - - if(!reload) { - if(_updater.LatestVersion > _version) { - _this.getUtils().execJs('alert("An update for BetterDiscord is available(v'+ _updater.LatestVersion +')! Download the latest version from GitHub!")'); +function getHash(callback) { + getUtils().download("api.github.com", "/repos/" + _cfg.repo + "/BetterDiscordApp/commits/" + _cfg.branch, function(data) { + try { + _cfg.hash = JSON.parse(data).sha; + getUtils().injectVar("_bdhash", _cfg.hash); + }catch(err) { + callback(false); + return; + } + if(_cfg.hash == undefined) { + callback(false); + return; } - } - - //Create loading element - _this.getUtils().execJs('var loadingNode = document.createElement("DIV");'); - _this.getUtils().execJs('loadingNode.innerHTML = \'
BetterDiscord - Loading Libraries :
\''); - _this.getUtils().execJs('var flex = document.getElementsByClassName("flex-vertical flex-spacer")[0]; flex.appendChild(loadingNode);'); - - //Create ipc - _this.getUtils().execJs("var betterDiscordIPC = require('ipc');"); - - - if(!reload) { - _this.getUtils().log("Hooking ipc async"); - _ipc.on('asynchronous-message', function(event, arg) { _this.ipcAsyncMessage(event, arg); }); - _this.getUtils().log("Hooked ipc async"); - } - - //Inject version - _this.getUtils().execJs('var version = "'+_version+'"'); - //Inject cdn - _this.getUtils().execJs('var bdcdn = "' + _updater.CDN + '";'); - //Load jQuery - _this.getUtils().updateLoading("Loading Resources(jQuery)", 0, 100); - _this.getUtils().injectJavaScriptSync("//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js", "load-jQueryUI"); - -}; -var loadCount = 0; -var libCount; -var loadUs; + getUtils().log("Hash: " + _cfg.hash); -BetterDiscord.prototype.updateData = function() { - libCount = 9; - loadUs = { - 'load-jQueryUI': { + callback(true); + }); +} + +function initUpdater() { + getUtils().log("Getting updater"); + getUtils().attempt(getUpdater, 3, 0, "Failed to load updater", waitForDom, function() { + exit("Failed to load updater after 3 attempts"); + }); +} + +function getUpdater(callback) { + getUtils().download("raw.githubusercontent.com", "/" + _cfg.repo + "/BetterDiscordApp/" + _cfg.hash + "/data/updater.json", function(data) { + try { + _cfg.updater = JSON.parse(data); + } catch(err) { + callback(false); + return; + } + + if(_cfg.updater == undefined) { + callback(false); + return; + } + + if(_cfg.updater.LatestVersion == undefined || _cfg.updater.CDN == undefined) { + callback(false); + return; + } + + getUtils().log("Latest Version: " + _cfg.updater.LatestVersion); + getUtils().log("Using CDN: " + _cfg.updater.CDN); + updateExtData(); + callback(true); + }); +} + +function updateExtData() { + getUtils().log("Updating ext data"); + + _extData = { + 'load-jQueryCookie': { 'type': 'javascript', - 'resource': 'jQueryUI', + 'resource': 'jQueryCookie', 'domain': 'cdnjs.cloudflare.com', 'url': '//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js', 'localurl': null, @@ -320,9 +158,9 @@ BetterDiscord.prototype.updateData = function() { 'load-mainCSS': { 'type': 'css', 'resource': 'Main CSS', - 'domain': _updater.CDN, - 'url': '//' + _updater.CDN + '/' + _repo + '/BetterDiscordApp/' + _hash + '/css/main.min.css', - 'localurl': _localServer + '/BetterDiscordApp/css/main.css', + 'domain': _cfg.updater.CDN, + 'url': '//' + _cfg.updater.CDN + '/' + _cfg.repo + '/BetterDiscordApp/' + _cfg.hash + '/css/main.min.css', + 'localurl': _cfg.localServer + '/BetterDiscordApp/css/main.css', 'message': 'load-mainJS', 'cacheable': false, 'variable': null @@ -330,18 +168,18 @@ BetterDiscord.prototype.updateData = function() { 'load-mainJS': { 'type': 'javascript', 'resource': 'Main JS', - 'domain': _updater.CDN, - 'url': '//' + _updater.CDN + '/' + _repo + '/BetterDiscordApp/' + _hash + '/js/main.min.js', - 'localurl': _localServer + '/BetterDiscordApp/js/main.js', - 'message': 'load-publicServers', + 'domain': _cfg.updater.CDN, + 'url': '//' + _cfg.updater.CDN + '/' + _cfg.repo + '/BetterDiscordApp/' + _cfg.hash + '/js/main.min.js', + 'localurl': _cfg.localServer + '/BetterDiscordApp/js/main.js', + 'message': 'load-emoteData-twitchGlobal', 'cacheable': false, 'variable': null }, 'load-publicServers': { 'type': 'json', 'resource': 'Public Servers', - 'domain': _updater.CDN, - 'url': '/' + _repo + '/BetterDiscordApp/' + _hash + '/data/serverlist.json', + 'domain': _cfg.updater.CDN, + 'url': '/' + _cfg.repo + '/BetterDiscordApp/' + _cfg.hash + '/data/serverlist.json', 'localurl': null, 'message': 'load-emoteData-twitchGlobal', 'cacheable': false, @@ -356,7 +194,7 @@ BetterDiscord.prototype.updateData = function() { 'message': 'load-emoteData-twitchSub', 'cacheable': true, 'variable': 'emotesTwitch', - 'localpath': _dataPath + "/emotes_twitch_global.json", + 'localpath': _cfg.dataPath + "/emotes_twitch_global.json", 'encoding': "utf8", 'https': true, 'parse': false, @@ -367,13 +205,13 @@ BetterDiscord.prototype.updateData = function() { 'load-emoteData-twitchGlobal-fallback': { 'type': 'emotedata', 'resource': 'Twitch Global Emotedata', - 'domain': _updater.CDN, - 'url': '/' + _repo + '/BetterDiscordApp/' + _hash + '/data/emotedata_twitch_global.json', + 'domain': _cfg.updater.CDN, + 'url': '/' + _cfg.repo + '/BetterDiscordApp/' + _cfg.hash + '/data/emotedata_twitch_global.json', 'localurl': null, 'message': 'load-emoteData-twitchSub', 'cacheable': true, 'variable': 'emotesTwitch', - 'localpath': _dataPath + "/emotes_twitch_global.json", + 'localpath': _cfg.dataPath + "/emotes_twitch_global.json", 'encoding': "utf8", 'https': true, 'parse': false, @@ -390,7 +228,7 @@ BetterDiscord.prototype.updateData = function() { 'message': 'load-emoteData-ffz', 'cacheable': true, 'variable': 'subEmotesTwitch', - 'localpath': _dataPath + "/emotes_twitch_subscriber.json", + 'localpath': _cfg.dataPath + "/emotes_twitch_subscriber.json", 'encoding': "utf8", 'https': true, 'parse': true, @@ -401,13 +239,13 @@ BetterDiscord.prototype.updateData = function() { 'load-emoteData-twitchSub-fallback': { 'type': 'emotedata', 'resource': 'Twitch Subscriber Emotedata', - 'domain': _updater.CDN, - 'url': '/' + _repo + '/BetterDiscordApp/' + _hash + '/data/emotedata_twitch_subscriber.json', + 'domain': _cfg.updater.CDN, + 'url': '/' + _cfg.repo + '/BetterDiscordApp/' + _cfg.hash + '/data/emotedata_twitch_subscriber.json', 'localurl': null, 'message': 'load-emoteData-ffz', 'cacheable': true, 'variable': 'subEmotesTwitch', - 'localpath': _dataPath + "/emotes_twitch_subscriber.json", + 'localpath': _cfg.dataPath + "/emotes_twitch_subscriber.json", 'encoding': "utf8", 'https': true, 'parse': true, @@ -418,13 +256,13 @@ BetterDiscord.prototype.updateData = function() { 'load-emoteData-ffz': { 'type': 'emotedata', 'resource': 'FrankerFaceZ Emotedata', - 'domain': _updater.CDN, - 'url': '/' + _repo + '/BetterDiscordApp/' + _hash + '/data/emotedata_ffz.json', + 'domain': _cfg.updater.CDN, + 'url': '/' + _cfg.repo + '/BetterDiscordApp/' + _cfg.hash + '/data/emotedata_ffz.json', 'localurl': null, 'message': 'load-emoteData-bttv', 'cacheable': true, 'variable': 'emotesFfz', - 'localpath': _dataPath + "/emotes_ffz.json", + 'localpath': _cfg.dataPath + "/emotes_ffz.json", 'encoding': "utf8", 'https': true, 'parse': true, @@ -441,7 +279,7 @@ BetterDiscord.prototype.updateData = function() { 'message': 'load-emoteData-bttv-2', 'cacheable': true, 'variable': 'emotesBTTV', - 'localpath': _dataPath + "/emotes_bttv.json", + 'localpath': _cfg.dataPath + "/emotes_bttv.json", 'encoding': "utf8", 'https': true, 'parse': false, @@ -452,13 +290,13 @@ BetterDiscord.prototype.updateData = function() { 'load-emoteData-bttv-2': { 'type': 'emotedata', 'resource': 'BTTV Emotedata', - 'domain': _updater.CDN, - 'url': '/' + _repo + '/BetterDiscordApp/' + _hash + '/data/emotedata_bttv.json', + 'domain': _cfg.updater.CDN, + 'url': '/' + _cfg.repo + '/BetterDiscordApp/' + _cfg.hash + '/data/emotedata_bttv.json', 'localurl': null, 'message': 'start-bd', 'cacheable': true, 'variable': 'emotesBTTV2', - 'localpath': _dataPath + "/emotes_bttv_2.json", + 'localpath': _cfg.dataPath + "/emotes_bttv_2.json", 'encoding': "utf8", 'https': true, 'parse': false, @@ -469,122 +307,385 @@ BetterDiscord.prototype.updateData = function() { }; } -BetterDiscord.prototype.ipcAsyncMessage = function(event, arg) { +function hook() { + try { + var webContents = getUtils().getWebContents(); - if(loadUs.hasOwnProperty(arg)) { - loadCount++; - var loadMe = loadUs[arg]; - _this.getUtils().updateLoading("Loading Resources (" + loadMe.resource + ")", loadCount / libCount * 100, 100); + getUtils().log("Hooking dom-ready"); + webContents.on('dom-ready', domReady); - var url = loadMe.url; - if(_local && loadMe.localurl != null) { - url = loadMe.localurl; + webContents.on('did-finish-loading', function() { + if(domReadyHooked) { + return; + } + getUtils().log("Hooking did-finish-loading failsafe"); + domReady(); + getUtils().log("Hooked did-finish-loading failsafe"); + }); + + }catch(err) { + exit(err); + } +} + +function waitForDom() { + if(!domReadyHooked) { + setTimeout(waitForDom, 1000); + return; + } + ipcHooked = true; + load(false); +} + +var domReadyHooked = false; +var ipcHooked = false; + +function domReady() { + getUtils().log("Hooked dom-ready"); + domReadyHooked = true; + if(ipcHooked) { + load(true); + } +} + +function load(reload) { + getUtils().log(reload ? "Reloading" : "Loading"); + getUtils().execJs("var betterDiscordIPC = require('electron').ipcRenderer;"); + if(!reload) { + if(_cfg.updater.LatestVersion > _cfg.version) { + getUtils().alert("Update Available", "An update for BetterDiscord is available("+_cfg.updater.LatestVersion+")! BetterDiscord.net"); + } + getUtils().log("Hooking ipc async"); + _bdIpc.on('asynchronous-message', function(event, arg) { ipcAsyncMessage(event, arg); }); + getUtils().log("Hooked ipc async"); + } + initLoaders(); +} + +function initLoaders() { + try { + getUtils().mkdirSync(_cfg.dataPath); + getUtils().mkdirSync(_cfg.dataPath + "plugins/"); + getUtils().mkdirSync(_cfg.dataPath + "themes/"); + getUtils().execJs('var themesupport2 = true'); + + loadPlugins(); + loadThemes(); + loadApp(); + }catch(err) { + exit(err); + } +} + +function loadPlugins() { + var pluginPath = _cfg.dataPath + "plugins/"; + _fs.readdir(pluginPath, function(err, files) { + if(err) { + getUtils().log(err); + getUtils().alert(err); + return; } - if(loadMe.type == 'javascript') { - _this.getUtils().injectJavaScriptSync(url, loadMe.message); - }else if(loadMe.type == 'css') { - _this.getUtils().injectStylesheetSync(url, loadMe.message); - }else if(loadMe.type == 'json') { - _this.getUtils().download(loadMe.domain, loadMe.url, function(data) { - _this.getUtils().execJs('var ' + loadMe.variable + ' = ' + data + ';'); - _this.getUtils().sendIcpAsync(loadMe.message); - }); - }else if(loadMe.type == 'emotedata') { - if(loadMe.variable != "emotesTwitch") { - _this.getUtils().execJs('var ' + loadMe.variable + ' = {};');//jic - } - var exists = _fs.existsSync(loadMe.localpath); + var pluginErrors = []; - if(exists && !_cacheExpired && loadMe.cacheable) { - _this.loadLocalEData(loadMe); - //_this.injectEmoteData(loadMe, _fs.readFileSync(loadMe.localpath, loadMe.encoding)); - } else { - _this.loadRemoteEData(loadMe); + getUtils().injectVarRaw("bdplugins", "{}"); + + files.forEach(function(fileName) { + if(!fileName.endsWith(".plugin.js")) { + getUtils().log("Invalid plugin detected: " + fileName); + return; } + + var plugin = _fs.readFileSync(pluginPath + fileName, 'utf8'); + var meta = plugin.split(_eol)[0]; + + if (meta.indexOf('META') < 0) { + getUtils().warn('Plugin META not found in file: ' + fileName); + pluginErrors.push(fileName + " Reason: Plugin META not found"); + return; + } + var pluginVar = meta.substring(meta.lastIndexOf('//META') + 6, meta.lastIndexOf('*//')); + var parse; + try { + parse = JSON.parse(pluginVar); + }catch(err) { + getUtils().warn("Failed to parse plugin META in file: " + fileName + "("+err+")"); + pluginErrors.push(fileName + " Reason: Failed to parse plugin META (" + err + ")"); + return; + } + + if(parse["name"] == undefined) { + getUtils().warn("Undefined plugin name in file: " + fileName); + pluginErrors.push(fileName + " Reason: invalid plugin name"); + return; + } + + getUtils().log("Loading plugin: " + parse["name"]); + getUtils().execJs(plugin); + getUtils().execJs('(function() { var plugin = new ' + parse["name"] + '(); bdplugins[plugin.getName()] = { "plugin": plugin, "enabled": false } })();') + }); + + if(pluginErrors.length > 0) { + getUtils().alert("The following plugin(s) could not be loaded", pluginErrors.join("
")); } + + }); +} + +function loadThemes() { + var themePath = _cfg.dataPath + "themes/"; + _fs.readdir(themePath, function(err, files) { + if(err) { + getUtils().log(err); + getUtils().alert(err); + return; + } + + var themeErrors = []; + + getUtils().injectVarRaw("bdthemes", "{}"); + + files.forEach(function(fileName) { + if(!fileName.endsWith(".theme.css")) { + getUtils().log("Invalid theme detected " + fileName); + return; + } + var theme = _fs.readFileSync(themePath + fileName, 'utf8'); + var split = theme.split(_eol); + var meta = split[0]; + if(meta.indexOf('META') < 0) { + getUtils().warn("Theme META not found in file: " + fileName); + themeErrors.push(fileName + " Reason: Theme META not found"); + return; + } + var themeVar = meta.substring(meta.lastIndexOf('//META') + 6, meta.lastIndexOf('*//')); + var themeInfo; + try { + themeInfo = JSON.parse(themeVar); + }catch(err) { + getUtils().warn("Failed to parse theme META in file: " + fileName + "("+err+")"); + themeErrors.push(fileName + " Reason: Failed to parse theme META (" + err + ")"); + return; + } + + if(themeInfo['name'] == undefined) { + getUtils().warn("Missing theme name in file: " + fileName); + themeErrors.push(fileName + " Reason: Missing theme name"); + return; + } + if(themeInfo['author'] == undefined) { + themeInfo['author'] = "Unknown"; + getUtils().warn("Missing author name in file: " + fileName); + } + if(themeInfo['description'] == undefined) { + themeInfo['description'] = "No_Description"; + getUtils().warn("Missing description in file: " + fileName); + } + if(themeInfo['version'] == undefined) { + themeInfo['version'] = "Unknown"; + getUtils().warn("Missing version in file: " + fileName); + } + + getUtils().log("Loading theme: " + themeInfo['name']); + split.splice(0, 1); + theme = split.join(_eol); + theme = theme.replace(/(\r\n|\n|\r)/gm, ''); + + _mainWindow.webContents.executeJavaScript('(function() { bdthemes["' + themeInfo['name'] + '"] = { "enabled": false, "name": "' + themeInfo['name'] + '", "css": "' + escape(theme) + '", "description": "' + themeInfo['description'] + '", "author":"' + themeInfo['author'] + '", "version":"' + themeInfo['version'] + '" } })();'); + }); + + if(themeErrors.length > 0) { + getUtils().alert("The following theme(s) could not be loaded", themeErrors.join("
")); + } + }); +} + +function loadApp() { + getUtils().execJs('var loadingNode = document.createElement("DIV");'); + getUtils().execJs('loadingNode.innerHTML = \'
BetterDiscord - Loading Libraries :
\''); + getUtils().execJs('var flex = document.getElementsByClassName("flex-vertical flex-spacer")[0]; flex.appendChild(loadingNode);'); + getUtils().injectVar('bdVersion', _cfg.version); + getUtils().injectVar('bdCdn', _cfg.CDN); + + getUtils().updateLoading("Loading Resource (jQuery)", 0, 100); + getUtils().injectJavaScriptSync("//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js", "load-jQueryCookie"); +} + +function ipcAsyncMessage(event, arg) { + + if(typeof(arg) === "object") { + switch(arg.arg) { + case "opendir": + if(arg.path == "plugindir") { + getUtils().openDir(_cfg.dataPath + "/plugins"); + break; + } + if(arg.path == "themedir") { + getUtils().openDir(_cfg.dataPath + "/themes"); + break; + } + if(arg.path == "datadir") { + getUtils().openDir(_cfg.dataPath); + break; + } + getUtils().openDir(arg.path); + break; + } + return; + } + + if(_extData.hasOwnProperty(arg)) { + loadExtData(_extData[arg]); } if(arg == "start-bd") { - _this.getUtils().saveLogs(_dataPath); - _this.getUtils().updateLoading("Starting Up", 100, 100); - _this.getUtils().execJs('var mainCore; var startBda = function() { mainCore = new Core(); mainCore.init(); }; startBda();'); + getUtils().updateLoading("Starting Up", 100, 100); + getUtils().execJs('var mainCore; var startBda = function() { mainCore = new Core(); mainCore.init(); }; startBda();'); //Remove loading node setTimeout(function() { - _this.getUtils().execJs('$("#bd-status").parent().parent().hide();'); + getUtils().execJs('$("#bd-status").parent().parent().hide();'); }, 2000); + getUtils().saveLogs(_cfg.dataPath); } -}; +} -BetterDiscord.prototype.loadRemoteEData = function(loadMe) { - _this.getUtils().log("Downloading " + loadMe.resource + " from " + loadMe.domain + loadMe.url ); - if(loadMe.https) { - _this.getUtils().download(loadMe.domain, loadMe.url, function(data) { - var parsedEmoteData = _this.parseEmoteData(loadMe, data); +var loadCounter = 0; +function loadExtData(extData) { + + loadCounter++; + + getUtils().updateLoading("Loading Resource (" + extData.resource + ")", loadCounter / Object.keys(_extData).length * 100, 100); + + var url = (_cfg.local && extData.localurl != null) ? extData.localurl : extData.url; + + try { + switch(extData.type) { + case 'javascript': + getUtils().injectJavaScriptSync(url, extData.message); + break; + case 'css': + getUtils().injectStylesheetSync(url, extData.message); + break; + case 'json': + getUtils().download(extData.domain, extData.url, function(data) { + getUtils().injectVar(extData.variable, data); + getUtils().sendIcpAsync(extData.message); + }); + break; + case 'emotedata': + if(extData.variable != "emotesTwitch") { + getUtils().injectVarRaw(extData.variable, "{}"); + } + var exists = _fs.existsSync(extData.localpath); + if(exists && !_cfg.cache.expired && extData.cacheable) { + loadEmoteData(extData, true); + } else { + loadEmoteData(extData, false); + } + break; + } + }catch(err) { + getUtils().warn(err); + getUtils().alert("Something went wrong :( Attempting to run.", err); + getUtils().sendIcpAsync(extData.message); + } +} + +function loadEmoteData(extData, local) { + if(local) { + getUtils().log("Reading " + extData.resource + " from file"); + var data = _fs.readFileSync(extData.localpath, extData.encoding); + + if(testJSON(extData, data)) { + injectEmoteData(extData, data); + } else { + getUtils().log("Deleting cached file " + extData.resource); + _fs.unlinkSync(extData.localpath); + getUtils().sendIcpAsync(extData.self); + } + return; + } + + if(extData.https) { + getUtils().download(extData.domain, extData.url, function(data) { + var parsedEmoteData = parseEmoteData(extData, data); if(parsedEmoteData == null) { - _this.getUtils().sendIcpAsync(loadMe.fallback); + getUtils().sendIcpAsync(extData.fallback); return true; } - _this.saveEmoteData(loadMe, parsedEmoteData); - _this.injectEmoteData(loadMe, parsedEmoteData); + saveEmoteData(extData, parsedEmoteData); + injectEmoteData(extData, parsedEmoteData); }); - return true; + return; } - _this.getUtils().downloadHttp(loadMe.url, function(data) { - var parsedEmoteData = _this.parseEmoteData(loadMe, data); - _this.saveEmoteData(loadMe, parsedEmoteData); - _this.injectEmoteData(loadMe, parsedEmoteData); + getUtils().downloadHttp(extData.url, function(data) { + var parsedEmoteData = parseEmoteData(extData, data); + if(parsedEmoteData == null) { + getUtils().sendIcpAsync(extData.fallback); + return true; + } + saveEmoteData(extData, parsedEmoteData); + injectEmoteData(extData, parsedEmoteData); }); -}; +} -BetterDiscord.prototype.loadLocalEData = function(loadMe) { - _this.getUtils().log("Reading " + loadMe.resource + " from file"); - var data = _fs.readFileSync(loadMe.localpath, loadMe.encoding); - - if(_this.testJSON(loadMe, data)) { - _this.injectEmoteData(loadMe, data); - } else { - _this.getUtils().log("Deleting cached file " + loadMe.resource); - _fs.unlinkSync(loadMe.localpath); - _this.getUtils().sendIcpAsync(loadMe.self); - } -}; - -BetterDiscord.prototype.testJSON = function(loadMe, data) { - _this.getUtils().log("Validating " + loadMe.resource); +function testJSON(extData, data) { + getUtils().log("Validating " + extData.resource); try { var json = JSON.parse(data); - _this.getUtils().log(loadMe.resource + " is valid"); + getUtils().log(extData.resource + " is valid"); return true; }catch(err) { - _this.getUtils().log(loadMe.resource + " is invalid"); + getUtils().warn(extData.resource + " is invalid"); return false; } -}; + return false; +} -BetterDiscord.prototype.parseEmoteData = function(loadMe, emoteData) { +function injectEmoteData(extData, data) { + if(data == null) { + getUtils().sendIcpAsync(extData.message); + return; + } - _this.getUtils().log("Parsing: " + loadMe.resource); + if(data.parse) { + getUtils().injectVarRaw(extData.variable, 'JSON.parse(\'' + data + '\');'); + } else { + getUtils().injectVarRaw(extData.variable, data); + } + + getUtils().sendIcpAsync(extData.message); +} + +function saveEmoteData(extData, data) { + try { + getUtils().log("Saving resource to file " + extData.resource); + _fs.writeFileSync(extData.localpath, data, extData.encoding); + } catch(err) { + getUtils().err("Failed to save resource to file " + extData.resource); + } +} + +function parseEmoteData(extData, data) { + getUtils().log("Parsing: " + extData.resource); var returnData; - switch(loadMe.specialparser) { - + switch(extData.specialparser) { case 0: //Twitch Global Emotes - //returnData = emoteData.replace(/\$/g, "\\$").replace(/'/g, "\\'").replace(/"/g, "\\\""); We don't need this anymore - return emoteData; + return data; break; case 1: //Twitch Subscriber Emotes returnData = {}; - if(!_this.testJSON(loadMe, emoteData)) { + if(!testJSON(extData, data)) { return null; } - emoteData = JSON.parse(emoteData); + data = JSON.parse(data); - var channels = emoteData["channels"]; + var channels = data["channels"]; for(var channel in channels) { var emotes = channels[channel]["emotes"]; for(var i = 0 ; i < emotes.length ; i++) { @@ -597,19 +698,19 @@ BetterDiscord.prototype.parseEmoteData = function(loadMe, emoteData) { returnData = JSON.stringify(returnData); break; case 2: //FFZ Emotes - returnData = emoteData; + returnData = data; break; case 3: //BTTV Emotes returnData = {}; - if(!_this.testJSON(loadMe, emoteData)) { + if(!testJSON(extData, data)) { return null; } - emoteData = JSON.parse(emoteData); + data = JSON.parse(data); - for(var emote in emoteData.emotes) { - emote = emoteData.emotes[emote]; + for(var emote in data.emotes) { + emote = data.emotes[emote]; var url = emote.url; var code = emote.regex; @@ -619,25 +720,23 @@ BetterDiscord.prototype.parseEmoteData = function(loadMe, emoteData) { returnData = JSON.stringify(returnData); break; case 4: - returnData = emoteData; + returnData = data; break; } return returnData; -}; +} -BetterDiscord.prototype.saveEmoteData = function(loadMe, emoteData) { - _fs.writeFileSync(loadMe.localpath, emoteData, loadMe.encoding); -}; +function getUtils() { + return _utils2; +} -BetterDiscord.prototype.injectEmoteData = function(loadMe, emoteData) { - if(emoteData != null) { - if(loadMe.parse) { - _this.getUtils().execJs('var ' + loadMe.variable + ' = JSON.parse(\'' + emoteData + '\');'); - } else { - _this.getUtils().execJs('var ' + loadMe.variable + ' = ' + emoteData + ';'); - } - } - _this.getUtils().sendIcpAsync(loadMe.message); -}; +function exit(reason) { + _error = true; + getUtils().log("Exiting. Reason: " + reason); + getUtils().saveLogs(_cfg.dataPath); + getUtils().alert("Something went wrong :(", reason); +} + +BetterDiscord.prototype.init = function() {}//Compatibility exports.BetterDiscord = BetterDiscord; \ No newline at end of file diff --git a/lib/Utils.js b/lib/Utils.js index 8d2950ca..ba00d379 100644 --- a/lib/Utils.js +++ b/lib/Utils.js @@ -1,8 +1,8 @@ /* BetterDiscordApp Utils and Helper functions - * Version: 1.4 + * Version: 1.5 * Author: Jiiks | http://jiiks.net * Date: 25/08/2015 - 09:19 - * Last Updated: 20/04/2016 + * Last Updated: 06/05/2016 * https://github.com/Jiiks/BetterDiscordApp */ @@ -224,7 +224,7 @@ Utils.prototype.mkdirSync = function(path) { } }; -Utils.prototype.try = function(func, attempts, attempt, message, success, err) { +Utils.prototype.attemptSync = function(func, attempts, attempt, message, success, err) { var self = this; attempt = attempt || 0; attempt++; @@ -235,9 +235,8 @@ Utils.prototype.try = function(func, attempts, attempt, message, success, err) { } setTimeout(function() { - self.warn(message + ", retrying #" + attempt); - if(!func()) { + self.warn(message + ", retrying #" + attempt); self.try(func, attempts, attempt, message, success, err); return; } @@ -246,6 +245,28 @@ Utils.prototype.try = function(func, attempts, attempt, message, success, err) { }, 1000); }; +Utils.prototype.attempt = function(func, attempts, attempt, message, success, err) { + var self = this; + attempt = attempt || 0; + attempt++; + + if(attempt > attempts) { + err(); + return; + } + + setTimeout(function() { + func(function(ok) { + if(!ok) { + self.warn(message + ", retrying #" + attempt); + self.try(func, attempts, attempt, message, success, err); + return; + } + success(); + }); + }, 1000); +}; + Utils.prototype.openDir = function(path) { switch(process.platform) { case "win32": diff --git a/lib/config.json b/lib/config.json index 4f3e5493..41ac52da 100644 --- a/lib/config.json +++ b/lib/config.json @@ -23,7 +23,7 @@ "repo": "Jiiks", "branch": null, "beta": false, - "local": true, + "local": false, "localServer": "http://localhost", "version": "0.2.8", "updater": null,