diff --git a/appasar/ptb/app_bootstrap/Constants.js b/appasar/ptb/app_bootstrap/Constants.js new file mode 100644 index 0000000..3514e17 --- /dev/null +++ b/appasar/ptb/app_bootstrap/Constants.js @@ -0,0 +1,32 @@ +"use strict"; + +// bootstrap constants +// after startup, these constants will be merged into core module constants +// since they are used in both locations (see app/Constants.js) +const { + releaseChannel +} = require('./buildInfo'); + +const { + getSettings +} = require('./appSettings'); + +const settings = getSettings(); + +function capitalizeFirstLetter(s) { + return s.charAt(0).toUpperCase() + s.slice(1); +} + +const APP_NAME = 'Discord' + (releaseChannel === 'stable' ? '' : capitalizeFirstLetter(releaseChannel)); +const APP_ID_BASE = 'com.squirrel'; +const APP_ID = `${APP_ID_BASE}.${APP_NAME}.${APP_NAME}`; +const API_ENDPOINT = settings.get('API_ENDPOINT') || 'https://discord.com/api'; +const UPDATE_ENDPOINT = settings.get('UPDATE_ENDPOINT') || API_ENDPOINT; +const NEW_UPDATE_ENDPOINT = settings.get('NEW_UPDATE_ENDPOINT') || 'https://discord.com/api/updates/'; +module.exports = { + APP_NAME, + APP_ID, + API_ENDPOINT, + NEW_UPDATE_ENDPOINT, + UPDATE_ENDPOINT +}; \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/GPUSettings.js b/appasar/ptb/app_bootstrap/GPUSettings.js new file mode 100644 index 0000000..734a2f4 --- /dev/null +++ b/appasar/ptb/app_bootstrap/GPUSettings.js @@ -0,0 +1,16 @@ +"use strict"; + +// this file is here for two reasons: +// 1. web requires ./GPUSettings file from electron app (bad!), and requires are +// relative to process.main (bootstrap's index.js) +// 2. GPUSettings has been refactored into GPUSettings, and because we want to +// be able to update GPUSettings OTA, we will have the core module provide +// us with the GPUSettings +// so tl;dr this is core module's GPUSettings, providing compat for web +exports.replace = function (GPUSettings) { + // replacing module.exports directly would have no effect, since requires are cached + // so we mutate the existing object + for (const name of Object.keys(GPUSettings)) { + exports[name] = GPUSettings[name]; + } +}; \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/appSettings.js b/appasar/ptb/app_bootstrap/appSettings.js new file mode 100644 index 0000000..9db2ef8 --- /dev/null +++ b/appasar/ptb/app_bootstrap/appSettings.js @@ -0,0 +1,27 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.init = init; +exports.getSettings = getSettings; + +var _Settings = _interopRequireDefault(require("../common/Settings")); + +var paths = _interopRequireWildcard(require("../common/paths")); + +function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +let settings; + +function init() { + settings = new _Settings.default(paths.getUserData()); +} + +function getSettings() { + return settings; +} \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/appUpdater.js b/appasar/ptb/app_bootstrap/appUpdater.js new file mode 100644 index 0000000..8341cce --- /dev/null +++ b/appasar/ptb/app_bootstrap/appUpdater.js @@ -0,0 +1,67 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.update = update; +exports.focusSplash = focusSplash; + +var _fs = _interopRequireDefault(require("fs")); + +var _path = _interopRequireDefault(require("path")); + +var moduleUpdater = _interopRequireWildcard(require("../common/moduleUpdater")); + +var paths = _interopRequireWildcard(require("../common/paths")); + +var _updater = require("../common/updater"); + +var _appSettings = require("./appSettings"); + +var autoStart = _interopRequireWildcard(require("./autoStart")); + +var _buildInfo = _interopRequireDefault(require("./buildInfo")); + +var _errorHandler = require("./errorHandler"); + +var splashScreen = _interopRequireWildcard(require("./splashScreen")); + +var _Constants = require("./Constants"); + +function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// settings +const USE_PINNED_UPDATE_MANIFEST = 'USE_PINNED_UPDATE_MANIFEST'; + +function update(startMinimized, doneCallback, showCallback) { + const settings = (0, _appSettings.getSettings)(); + + if ((0, _updater.tryInitUpdater)(_buildInfo.default, _Constants.NEW_UPDATE_ENDPOINT)) { + const updater = (0, _updater.getUpdater)(); + const usePinnedUpdateManifest = settings.get(USE_PINNED_UPDATE_MANIFEST); + updater.on('host-updated', () => { + autoStart.update(() => {}); + }); + updater.on('unhandled-exception', _errorHandler.fatal); + + if (usePinnedUpdateManifest) { + const manifestPath = _path.default.join(paths.getUserData(), 'pinned_update.json'); + + updater.setPinnedManifestSync(JSON.parse(_fs.default.readFileSync(manifestPath))); + } + } else { + moduleUpdater.init(_Constants.UPDATE_ENDPOINT, settings, _buildInfo.default); + } + + splashScreen.initSplash(startMinimized); + splashScreen.events.once(splashScreen.APP_SHOULD_LAUNCH, doneCallback); + splashScreen.events.once(splashScreen.APP_SHOULD_SHOW, showCallback); +} + +function focusSplash() { + splashScreen.focusWindow(); +} \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/autoStart/darwin.js b/appasar/ptb/app_bootstrap/autoStart/darwin.js new file mode 100644 index 0000000..e56b6bf --- /dev/null +++ b/appasar/ptb/app_bootstrap/autoStart/darwin.js @@ -0,0 +1,25 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.install = install; +exports.update = update; +exports.isInstalled = isInstalled; +exports.uninstall = uninstall; + +function install(callback) { + return callback(); +} + +function update(callback) { + return callback(); +} + +function isInstalled(callback) { + return callback(false); +} + +function uninstall(callback) { + return callback(); +} \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/autoStart/index.js b/appasar/ptb/app_bootstrap/autoStart/index.js new file mode 100644 index 0000000..9264bb8 --- /dev/null +++ b/appasar/ptb/app_bootstrap/autoStart/index.js @@ -0,0 +1,3 @@ +"use strict"; + +module.exports = require('./' + process.platform); \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/autoStart/linux.js b/appasar/ptb/app_bootstrap/autoStart/linux.js new file mode 100644 index 0000000..ee3f180 --- /dev/null +++ b/appasar/ptb/app_bootstrap/autoStart/linux.js @@ -0,0 +1,92 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.install = install; +exports.update = update; +exports.isInstalled = isInstalled; +exports.uninstall = uninstall; + +var _fs = _interopRequireDefault(require("fs")); + +var _path = _interopRequireDefault(require("path")); + +var _electron = require("electron"); + +var _buildInfo = _interopRequireDefault(require("../buildInfo")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// TODO: We should use Constant's APP_NAME, but only once +// we set up backwards compat with this. +const appName = _path.default.basename(process.execPath, '.exe'); + +const exePath = _electron.app.getPath('exe'); + +const exeDir = _path.default.dirname(exePath); + +const iconPath = _path.default.join(exeDir, 'discord.png'); + +const autostartDir = _path.default.join(_electron.app.getPath('appData'), 'autostart'); + +const electronAppName = _electron.app.name ? _electron.app.name : _electron.app.getName(); + +const autostartFileName = _path.default.join(autostartDir, electronAppName + '-' + _buildInfo.default.releaseChannel + '.desktop'); + +const desktopFile = `[Desktop Entry] +Type=Application +Exec=${exePath} +Hidden=false +NoDisplay=false +Name=${appName} +Icon=${iconPath} +Comment=Text and voice chat for gamers. +X-GNOME-Autostart-enabled=true +`; + +function ensureDir() { + try { + _fs.default.mkdirSync(autostartDir); + + return true; + } catch (e) {// catch for when it already exists. + } + + return false; +} + +function install(callback) { + // TODO: This could fail. We should read its return value + ensureDir(); + + try { + return _fs.default.writeFile(autostartFileName, desktopFile, callback); + } catch (e) { + // I guess we don't autostart then + return callback(); + } +} + +function update(callback) { + // TODO: We might need to implement this later on + return callback(); +} + +function isInstalled(callback) { + try { + _fs.default.stat(autostartFileName, (err, stats) => { + if (err) { + return callback(false); + } + + return callback(stats.isFile()); + }); + } catch (e) { + return callback(false); + } +} + +function uninstall(callback) { + return _fs.default.unlink(autostartFileName, callback); +} \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/autoStart/win32.js b/appasar/ptb/app_bootstrap/autoStart/win32.js new file mode 100644 index 0000000..62292c6 --- /dev/null +++ b/appasar/ptb/app_bootstrap/autoStart/win32.js @@ -0,0 +1,69 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.install = install; +exports.update = update; +exports.isInstalled = isInstalled; +exports.uninstall = uninstall; + +var _path = _interopRequireDefault(require("path")); + +var windowsUtils = _interopRequireWildcard(require("../windowsUtils")); + +var _appSettings = require("../appSettings"); + +function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const settings = (0, _appSettings.getSettings)(); // TODO: We should use Constant's APP_NAME, but only once +// we set up backwards compat with this. + +const appName = _path.default.basename(process.execPath, '.exe'); + +const fullExeName = _path.default.basename(process.execPath); + +const updatePath = _path.default.join(_path.default.dirname(process.execPath), '..', 'Update.exe'); + +function install(callback) { + const startMinimized = settings.get('START_MINIMIZED', false); + let execPath = `${updatePath} --processStart ${fullExeName}`; + + if (startMinimized) { + execPath = `${execPath} --process-start-args --start-minimized`; + } + + const queue = [['HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run', '/v', appName, '/d', execPath]]; + windowsUtils.addToRegistry(queue, callback); +} + +function update(callback) { + isInstalled(installed => { + if (installed) { + install(callback); + } else { + callback(); + } + }); +} + +function isInstalled(callback) { + const queryValue = ['HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run', '/v', appName]; + queryValue.unshift('query'); + windowsUtils.spawnReg(queryValue, (error, stdout) => { + const doesOldKeyExist = stdout.indexOf(appName) >= 0; + callback(doesOldKeyExist); + }); +} + +function uninstall(callback) { + const queryValue = ['HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run', '/v', appName, '/f']; + queryValue.unshift('delete'); + windowsUtils.spawnReg(queryValue, (error, stdout) => { + callback(); + }); +} \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/bootstrap.js b/appasar/ptb/app_bootstrap/bootstrap.js new file mode 100644 index 0000000..91dc435 --- /dev/null +++ b/appasar/ptb/app_bootstrap/bootstrap.js @@ -0,0 +1,213 @@ +"use strict"; + +// bootstrap, or what runs before the rest of desktop does +// responsible for handling updates and updating modules before continuing startup +if (process.platform === 'linux') { + // Some people are reporting audio problems on Linux that are fixed by setting + // an environment variable PULSE_LATENCY_MSEC=30 -- the "real" fix is to see + // what conditions require this and set this then (also to set it directly in + // our webrtc setup code rather than here) but this should fix the bug for now. + if (process.env.PULSE_LATENCY_MSEC === undefined) { + process.env.PULSE_LATENCY_MSEC = 30; + } +} + +const { + app, + Menu +} = require('electron'); + +const buildInfo = require('./buildInfo'); + +app.setVersion(buildInfo.version); // expose releaseChannel to a global, since it's used by splash screen + +global.releaseChannel = buildInfo.releaseChannel; + +const errorHandler = require('./errorHandler'); + +errorHandler.init(); + +const crashReporterSetup = require('../common/crashReporterSetup'); + +crashReporterSetup.init(buildInfo); + +const paths = require('../common/paths'); + +paths.init(buildInfo); +global.moduleDataPath = paths.getModuleDataPath(); + +const appSettings = require('./appSettings'); + +appSettings.init(); + +const Constants = require('./Constants'); + +const GPUSettings = require('./GPUSettings'); + +function setupHardwareAcceleration() { + const settings = appSettings.getSettings(); // TODO: this is a copy of gpuSettings.getEnableHardwareAcceleration + + if (!settings.get('enableHardwareAcceleration', true)) { + app.disableHardwareAcceleration(); + } +} + +setupHardwareAcceleration(); +app.allowRendererProcessReuse = false; // [adill] work around chrome 66 disabling autoplay by default + +app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required'); + +function hasArgvFlag(flag) { + return (process.argv || []).slice(1).includes(flag); +} + +console.log(`${Constants.APP_NAME} ${app.getVersion()}`); +let preventStartup = false; + +if (process.platform === 'win32') { + // this tells Windows (in particular Windows 10) which icon to associate your app with, important for correctly + // pinning app to task bar. + app.setAppUserModelId(Constants.APP_ID); + + const { + handleStartupEvent + } = require('./squirrelUpdate'); // TODO: Isn't using argv[1] fragile? + + + const squirrelCommand = process.argv[1]; // TODO: Should `Discord` be a constant in this case? It's a protocol. + // TODO: Is protocol case sensitive? + + if (handleStartupEvent('Discord', app, squirrelCommand)) { + preventStartup = true; + } +} + +const appUpdater = require('./appUpdater'); + +const moduleUpdater = require('../common/moduleUpdater'); + +const updater = require('../common/updater'); + +const splashScreen = require('./splashScreen'); + +const autoStart = require('./autoStart'); + +const requireNative = require('./requireNative'); + +let coreModule; +const isFirstInstance = app.requestSingleInstanceLock(); +const allowMultipleInstances = hasArgvFlag('--multi-instance'); + +if (!isFirstInstance && !allowMultipleInstances) { + app.quit(); +} + +function extractUrlFromArgs(args) { + const urlArgIndex = args.indexOf('--url'); + + if (urlArgIndex < 0) { + return null; + } + + const passThroughArgsIndex = args.indexOf('--'); + + if (passThroughArgsIndex < 0 || passThroughArgsIndex < urlArgIndex) { + return null; + } + + const url = args[passThroughArgsIndex + 1]; + + if (url == null) { + return null; + } + + return url; +} + +let initialUrl = extractUrlFromArgs(process.argv); + +if (!allowMultipleInstances) { + app.on('second-instance', (_event, args, _workingDirectory) => { + if (args != null && args.indexOf('--squirrel-uninstall') > -1) { + app.quit(); + return; + } + + const url = extractUrlFromArgs(args); + + if (coreModule) { + // url can be null, as a user opening the executable again will focus the app from background + coreModule.handleOpenUrl(url); + } else if (url != null) { + initialUrl = url; + } + + if (!coreModule) { + appUpdater.focusSplash(); + } + }); +} + +app.on('will-finish-launching', () => { + // on macos protocol links are handled entirely through this event + app.on('open-url', (event, url) => { + event.preventDefault(); + + if (coreModule) { + coreModule.handleOpenUrl(url); + } else { + initialUrl = url; + } + }); +}); + +function startUpdate() { + console.log('Starting updater.'); + const startMinimized = hasArgvFlag('--start-minimized'); + appUpdater.update(startMinimized, () => { + try { + coreModule = requireNative('discord_desktop_core'); + coreModule.startup({ + paths, + splashScreen, + moduleUpdater, + autoStart, + buildInfo, + appSettings, + Constants, + GPUSettings, + updater, + crashReporterSetup + }); + + if (initialUrl != null) { + coreModule.handleOpenUrl(initialUrl); + initialUrl = null; + } + } catch (err) { + return errorHandler.fatal(err); + } + }, () => { + coreModule.setMainWindowVisible(!startMinimized); + }); +} + +function startApp() { + console.log('Starting app.'); + paths.cleanOldVersions(buildInfo); + + const startupMenu = require('./startupMenu'); + + Menu.setApplicationMenu(startupMenu); + startUpdate(); +} + +if (preventStartup) { + console.log('Startup prevented.'); // TODO: shouldn't we exit out? +} else { + if (app.isReady()) { + startApp(); + } else { + app.once('ready', startApp); + } +} \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/buildInfo.js b/appasar/ptb/app_bootstrap/buildInfo.js new file mode 100644 index 0000000..1b4a7f3 --- /dev/null +++ b/appasar/ptb/app_bootstrap/buildInfo.js @@ -0,0 +1,16 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _path = _interopRequireDefault(require("path")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const buildInfo = require(_path.default.join(process.resourcesPath, 'build_info.json')); + +var _default = buildInfo; +exports.default = _default; +module.exports = exports.default; \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/data/quotes_copy.json b/appasar/ptb/app_bootstrap/data/quotes_copy.json new file mode 100644 index 0000000..fc33384 --- /dev/null +++ b/appasar/ptb/app_bootstrap/data/quotes_copy.json @@ -0,0 +1,14 @@ +[ + "Upsorbing the Contents", + "Additive Parsing the Load", + "Commence Monosaturated Goodening", + "Kick Off the Multi-Core Widening", + "Bastening the Game Turkey", + "Abstracting the Rummage Disc", + "Undecerealenizing the Process", + "Postrefragmenting the Widget Layer", + "Satisfying the Constraints", + "Abnoramalzing Some of the Matrices", + "Optimizing the People", + "Proclaigerizing the Network" +] diff --git a/appasar/ptb/app_bootstrap/errorHandler.js b/appasar/ptb/app_bootstrap/errorHandler.js new file mode 100644 index 0000000..b5a9b79 --- /dev/null +++ b/appasar/ptb/app_bootstrap/errorHandler.js @@ -0,0 +1,45 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.init = init; +exports.fatal = fatal; + +var _electron = require("electron"); + +function isErrorSafeToSuppress(error) { + return /attempting to call a function in a renderer window/i.test(error.message); +} + +function init() { + process.on('uncaughtException', error => { + const stack = error.stack ? error.stack : String(error); + const message = `Uncaught exception:\n ${stack}`; + console.warn(message); + + if (!isErrorSafeToSuppress(error)) { + _electron.dialog.showErrorBox('A JavaScript error occurred in the main process', message); + } + }); +} // show a similar error message to the error handler, except exit out the app +// after the error message has been closed + + +function fatal(err) { + const options = { + type: 'error', + message: 'A fatal Javascript error occured', + detail: err && err.stack ? err.stack : String(err) + }; + + const callback = _ => _electron.app.quit(); + + const electronMajor = parseInt(process.versions.electron.split('.')[0]); + + if (electronMajor >= 6) { + _electron.dialog.showMessageBox(null, options).then(callback); + } else { + _electron.dialog.showMessageBox(options, callback); + } +} \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/hostUpdater.js b/appasar/ptb/app_bootstrap/hostUpdater.js new file mode 100644 index 0000000..cdde78b --- /dev/null +++ b/appasar/ptb/app_bootstrap/hostUpdater.js @@ -0,0 +1,199 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _electron = require("electron"); + +var _events = require("events"); + +var _request = _interopRequireDefault(require("./request")); + +var squirrelUpdate = _interopRequireWildcard(require("./squirrelUpdate")); + +function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function versionParse(verString) { + return verString.split('.').map(i => parseInt(i)); +} + +function versionNewer(verA, verB) { + let i = 0; + + while (true) { + const a = verA[i]; + const b = verB[i]; + i++; + + if (a === undefined) { + return false; + } else { + if (b === undefined || a > b) { + return true; + } + + if (a < b) { + return false; + } + } + } +} + +class AutoUpdaterWin32 extends _events.EventEmitter { + constructor() { + super(); + this.updateUrl = null; + this.updateVersion = null; + } + + setFeedURL(updateUrl) { + this.updateUrl = updateUrl; + } + + quitAndInstall() { + if (squirrelUpdate.updateExistsSync()) { + squirrelUpdate.restart(_electron.app, this.updateVersion || _electron.app.getVersion()); + } else { + require('auto-updater').quitAndInstall(); + } + } + + downloadAndInstallUpdate(callback) { + squirrelUpdate.spawnUpdateInstall(this.updateUrl, progress => { + this.emit('update-progress', progress); + }).catch(err => callback(err)).then(() => callback()); + } + + checkForUpdates() { + if (this.updateUrl == null) { + throw new Error('Update URL is not set'); + } + + this.emit('checking-for-update'); + + if (!squirrelUpdate.updateExistsSync()) { + this.emit('update-not-available'); + return; + } + + squirrelUpdate.spawnUpdate(['--check', this.updateUrl], (error, stdout) => { + if (error != null) { + this.emit('error', error); + return; + } + + try { + // Last line of the output is JSON details about the releases + const json = stdout.trim().split('\n').pop(); + const releasesFound = JSON.parse(json).releasesToApply; + + if (releasesFound == null || releasesFound.length == 0) { + this.emit('update-not-available'); + return; + } + + const update = releasesFound.pop(); + this.emit('update-available'); + this.downloadAndInstallUpdate(error => { + if (error != null) { + this.emit('error', error); + return; + } + + this.updateVersion = update.version; + this.emit('update-downloaded', {}, update.release, update.version, new Date(), this.updateUrl, this.quitAndInstall.bind(this)); + }); + } catch (error) { + error.stdout = stdout; + this.emit('error', error); + } + }); + } + +} // todo + + +class AutoUpdaterLinux extends _events.EventEmitter { + constructor() { + super(); + this.updateUrl = null; + } + + setFeedURL(url) { + this.updateUrl = url; + } + + quitAndInstall() { + // Just restart. The splash screen will hit the update manually state and + // prompt the user to download the new package. + _electron.app.relaunch(); + + _electron.app.quit(); + } + + async checkForUpdates() { + const currVersion = versionParse(_electron.app.getVersion()); + this.emit('checking-for-update'); + + try { + const response = await _request.default.get(this.updateUrl); + + if (response.statusCode === 204) { + // you are up to date + this.emit('update-not-available'); + return; + } + + let latestVerStr = ''; + let latestVersion = []; + + try { + const latestMetadata = JSON.parse(response.body); + latestVerStr = latestMetadata.name; + latestVersion = versionParse(latestVerStr); + } catch (_) {} + + if (versionNewer(latestVersion, currVersion)) { + console.log('[Updates] You are out of date!'); // you need to update + + this.emit('update-manually', latestVerStr); + } else { + console.log('[Updates] You are living in the future!'); + this.emit('update-not-available'); + } + } catch (err) { + console.error('[Updates] Error fetching ' + this.updateUrl + ': ' + err.message); + this.emit('error', err); + } + } + +} + +let autoUpdater; // TODO +// events: checking-for-update, update-available, update-not-available, update-manually, update-downloaded, error +// also, checkForUpdates, setFeedURL, quitAndInstall +// also, see electron.autoUpdater, and its API + +switch (process.platform) { + case 'darwin': + autoUpdater = require('electron').autoUpdater; + break; + + case 'win32': + autoUpdater = new AutoUpdaterWin32(); + break; + + case 'linux': + autoUpdater = new AutoUpdaterLinux(); + break; +} + +var _default = autoUpdater; +exports.default = _default; +module.exports = exports.default; \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/images/img_lucky_dice.png b/appasar/ptb/app_bootstrap/images/img_lucky_dice.png new file mode 100644 index 0000000..909b729 Binary files /dev/null and b/appasar/ptb/app_bootstrap/images/img_lucky_dice.png differ diff --git a/appasar/ptb/app_bootstrap/index.js b/appasar/ptb/app_bootstrap/index.js new file mode 100644 index 0000000..df2d65c --- /dev/null +++ b/appasar/ptb/app_bootstrap/index.js @@ -0,0 +1,56 @@ +"use strict"; + +const { + app +} = require('electron'); + +const buildInfo = require('./buildInfo'); + +const paths = require('../common/paths'); + +paths.init(buildInfo); + +const moduleUpdater = require('../common/moduleUpdater'); + +const updater = require('../common/updater'); + +const requireNative = require('./requireNative'); + +function getAppMode() { + if (process.argv && process.argv.includes('--overlay-host')) { + return 'overlay-host'; + } + + return 'app'; +} + +const mode = getAppMode(); + +if (mode === 'app') { + require('./bootstrap'); +} else if (mode === 'overlay-host') { + // Initialize the update system just enough to find installed native modules. + const appSettings = require('./appSettings'); + + appSettings.init(); + + const { + NEW_UPDATE_ENDPOINT + } = require('./Constants'); + + if (buildInfo.newUpdater) { + if (!updater.tryInitUpdater(buildInfo, NEW_UPDATE_ENDPOINT)) { + throw new Error('Failed to initialize modules in overlay host.'); + } // Load the module search path but if there's a pending host update, don't + // restart into it. + + + updater.getUpdater().startCurrentVersionSync({ + allowObsoleteHost: true + }); + } else { + moduleUpdater.initPathsOnly(buildInfo); + } + + requireNative('discord_overlay2/standalone_host.js'); +} \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/installDevTools.js b/appasar/ptb/app_bootstrap/installDevTools.js new file mode 100644 index 0000000..6532a55 --- /dev/null +++ b/appasar/ptb/app_bootstrap/installDevTools.js @@ -0,0 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +// used in devtools to hook in additional dev tools +// require('electron').remote.require('./installDevTools')() +function installDevTools() { + console.log(`Installing Devtron`); + + const devtron = require('devtron'); + + devtron.uninstall(); + devtron.install(); + console.log(`Installed Devtron`); +} + +var _default = installDevTools; +exports.default = _default; +module.exports = exports.default; \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/ipcMain.js b/appasar/ptb/app_bootstrap/ipcMain.js new file mode 100644 index 0000000..2a2540d --- /dev/null +++ b/appasar/ptb/app_bootstrap/ipcMain.js @@ -0,0 +1,15 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _electron = require("electron"); + +var _default = { + on: (event, callback) => _electron.ipcMain.on(`DISCORD_${event}`, callback), + removeListener: (event, callback) => _electron.ipcMain.removeListener(`DISCORD_${event}`, callback) +}; +exports.default = _default; +module.exports = exports.default; \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/request.js b/appasar/ptb/app_bootstrap/request.js new file mode 100644 index 0000000..c5a7c71 --- /dev/null +++ b/appasar/ptb/app_bootstrap/request.js @@ -0,0 +1,186 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; + +var _electron = require("electron"); + +var _querystring = _interopRequireDefault(require("querystring")); + +var _request = _interopRequireDefault(require("request")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const DEFAULT_REQUEST_TIMEOUT = 30000; + +function makeHTTPResponse({ + method, + url, + headers, + statusCode, + statusMessage +}, body) { + return { + method, + url, + headers, + statusCode, + statusMessage, + body + }; +} + +function makeHTTPStatusError(response) { + const err = new Error(`HTTP Error: Status Code ${response.statusCode}`); + err.response = response; + return err; +} + +function handleHTTPResponse(resolve, reject, response, stream) { + const totalBytes = parseInt(response.headers['content-length'] || 1, 10); + let receivedBytes = 0; + const chunks = []; // don't stream response if it's a failure + + if (response.statusCode >= 300) { + stream = null; + } + + response.on('data', chunk => { + if (stream != null) { + receivedBytes += chunk.length; + stream.write(chunk); + stream.emit('progress', { + totalBytes, + receivedBytes + }); + return; + } + + chunks.push(chunk); + }); + response.on('end', () => { + if (stream != null) { + stream.on('finish', () => resolve(makeHTTPResponse(response, null))); + stream.end(); + return; + } + + const res = makeHTTPResponse(response, Buffer.concat(chunks)); + + if (res.statusCode >= 300) { + reject(makeHTTPStatusError(res)); + return; + } + + resolve(res); + }); +} + +function nodeRequest({ + method, + url, + headers, + qs, + timeout, + body, + stream +}) { + return new Promise((resolve, reject) => { + const req = (0, _request.default)({ + method, + url, + qs, + headers, + followAllRedirects: true, + encoding: null, + timeout: timeout != null ? timeout : DEFAULT_REQUEST_TIMEOUT, + body + }); + req.on('response', response => handleHTTPResponse(resolve, reject, response, stream)); + req.on('error', err => reject(err)); + }); +} + +async function electronRequest({ + method, + url, + headers, + qs, + timeout, + body, + stream +}) { + await _electron.app.whenReady(); + + const { + net, + session + } = require('electron'); + + const req = net.request({ + method, + url: `${url}${qs != null ? `?${_querystring.default.stringify(qs)}` : ''}`, + redirect: 'follow', + session: session.defaultSession + }); + + if (headers != null) { + for (const headerKey of Object.keys(headers)) { + req.setHeader(headerKey, headers[headerKey]); + } + } + + if (body != null) { + req.write(body, 'utf-8'); + } + + return new Promise((resolve, reject) => { + const reqTimeout = setTimeout(() => { + req.abort(); + reject(new Error(`network timeout: ${url}`)); + }, timeout != null ? timeout : DEFAULT_REQUEST_TIMEOUT); + req.on('login', (authInfo, callback) => callback()); + req.on('response', response => { + clearTimeout(reqTimeout); + handleHTTPResponse(resolve, reject, response, stream); + }); + req.on('error', err => { + clearTimeout(reqTimeout); + reject(err); + }); + req.end(); + }); +} + +async function requestWithMethod(method, options) { + if (typeof options === 'string') { + options = { + url: options + }; + } + + options = { ...options, + method + }; + + try { + return await electronRequest(options); + } catch (err) { + console.log(`Error downloading with electron net: ${err.message}`); + console.log('Falling back to node net library..'); + } + + return nodeRequest(options); +} // only supports get for now, since retrying is non-idempotent and +// we'd want to grovel the errors to make sure it's safe to retry + + +for (const method of ['get']) { + requestWithMethod[method] = requestWithMethod.bind(null, method.toUpperCase()); +} + +var _default = requestWithMethod; +exports.default = _default; +module.exports = exports.default; \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/requireNative.js b/appasar/ptb/app_bootstrap/requireNative.js new file mode 100644 index 0000000..c18c0c0 --- /dev/null +++ b/appasar/ptb/app_bootstrap/requireNative.js @@ -0,0 +1,5 @@ +"use strict"; + +// require(), with paths specialized for requiring only native modules. +module.paths = []; +module.exports = require; \ No newline at end of file diff --git a/appasar/ptb/app_bootstrap/splash/a934ab008c7f6a2274ec441f6be0696a.woff b/appasar/ptb/app_bootstrap/splash/a934ab008c7f6a2274ec441f6be0696a.woff new file mode 100644 index 0000000..98aa239 Binary files /dev/null and b/appasar/ptb/app_bootstrap/splash/a934ab008c7f6a2274ec441f6be0696a.woff differ diff --git a/appasar/ptb/app_bootstrap/splash/abddffb32a4a35627c3857a06c751424.png b/appasar/ptb/app_bootstrap/splash/abddffb32a4a35627c3857a06c751424.png new file mode 100644 index 0000000..909b729 Binary files /dev/null and b/appasar/ptb/app_bootstrap/splash/abddffb32a4a35627c3857a06c751424.png differ diff --git a/appasar/ptb/app_bootstrap/splash/d153359b5d87601d2b9c708b7ae2db02.woff b/appasar/ptb/app_bootstrap/splash/d153359b5d87601d2b9c708b7ae2db02.woff new file mode 100644 index 0000000..2a2f65a Binary files /dev/null and b/appasar/ptb/app_bootstrap/splash/d153359b5d87601d2b9c708b7ae2db02.woff differ diff --git a/appasar/ptb/app_bootstrap/splash/index.html b/appasar/ptb/app_bootstrap/splash/index.html new file mode 100644 index 0000000..5dec1de --- /dev/null +++ b/appasar/ptb/app_bootstrap/splash/index.html @@ -0,0 +1,11 @@ + + +
+ +L(A/m)&&T("overflow"),h*=m}var v=t.length+1;i=Q(s-c,v,0==c),L(s/v)>A-o&&T("overflow"),o+=L(s/v),s%=v,t.splice(s++,0,o)}return String.fromCodePoint.apply(String,t)},u=function(e){var r=[],t=(e=N(e)).length,a=128,s=0,o=72,i=!0,n=!1,l=void 0;try{for(var u,c=e[Symbol.iterator]();!(i=(u=c.next()).done);i=!0){var h=u.value;h<128&&r.push(z(h))}}catch(e){n=!0,l=e}finally{try{!i&&c.return&&c.return()}finally{if(n)throw l}}var d=r.length,f=d;for(d&&r.push("-");f