├── .gitignore
├── src
├── utils
│ ├── paths.js
│ ├── buildInfo.js
│ └── securityUtils.js
├── appSettings.js
├── splashScreen.js
├── images
│ ├── badges
│ │ ├── badge-1.ico
│ │ ├── badge-10.ico
│ │ ├── badge-11.ico
│ │ ├── badge-2.ico
│ │ ├── badge-3.ico
│ │ ├── badge-4.ico
│ │ ├── badge-5.ico
│ │ ├── badge-6.ico
│ │ ├── badge-7.ico
│ │ ├── badge-8.ico
│ │ └── badge-9.ico
│ ├── systemtray
│ │ ├── tray.png
│ │ ├── tray-muted.png
│ │ ├── tray-unread.png
│ │ ├── tray-connected.png
│ │ ├── tray-deafened.png
│ │ └── tray-speaking.png
│ ├── thumbar
│ │ ├── darwin
│ │ │ ├── mute.png
│ │ │ ├── video.png
│ │ │ ├── deafen.png
│ │ │ ├── deafen-off.png
│ │ │ ├── disconnect.png
│ │ │ ├── mute-off.png
│ │ │ └── video-off.png
│ │ └── win32
│ │ │ ├── deafen.png
│ │ │ ├── mute.png
│ │ │ ├── video.png
│ │ │ ├── mute-off.png
│ │ │ ├── deafen-off.png
│ │ │ ├── disconnect.png
│ │ │ ├── mute-light.png
│ │ │ ├── video-light.png
│ │ │ ├── video-off.png
│ │ │ ├── deafen-light.png
│ │ │ ├── deafen-off-light.png
│ │ │ ├── disconnect-light.png
│ │ │ ├── mute-off-light.png
│ │ │ └── video-off-light.png
│ ├── close.svg
│ └── discord.svg
├── bootstrapModules.js
├── ipcMain.js
├── index.js
├── utils.js
├── Constants.js
├── appBadge.js
├── systemTray.js
└── mainScreen.js
├── README.md
├── test.sh
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | *.asar
2 |
--------------------------------------------------------------------------------
/src/utils/paths.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../bootstrapModules').paths;
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OpenCore
2 | Open-source alternative of Discord's desktop_core
3 |
--------------------------------------------------------------------------------
/src/appSettings.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./bootstrapModules').appSettings;
--------------------------------------------------------------------------------
/src/splashScreen.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./bootstrapModules').splashScreen;
--------------------------------------------------------------------------------
/src/utils/buildInfo.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../bootstrapModules').buildInfo;
--------------------------------------------------------------------------------
/src/utils/securityUtils.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../bootstrapModules').securityUtils;
--------------------------------------------------------------------------------
/src/images/badges/badge-1.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/badges/badge-1.ico
--------------------------------------------------------------------------------
/src/images/badges/badge-10.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/badges/badge-10.ico
--------------------------------------------------------------------------------
/src/images/badges/badge-11.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/badges/badge-11.ico
--------------------------------------------------------------------------------
/src/images/badges/badge-2.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/badges/badge-2.ico
--------------------------------------------------------------------------------
/src/images/badges/badge-3.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/badges/badge-3.ico
--------------------------------------------------------------------------------
/src/images/badges/badge-4.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/badges/badge-4.ico
--------------------------------------------------------------------------------
/src/images/badges/badge-5.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/badges/badge-5.ico
--------------------------------------------------------------------------------
/src/images/badges/badge-6.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/badges/badge-6.ico
--------------------------------------------------------------------------------
/src/images/badges/badge-7.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/badges/badge-7.ico
--------------------------------------------------------------------------------
/src/images/badges/badge-8.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/badges/badge-8.ico
--------------------------------------------------------------------------------
/src/images/badges/badge-9.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/badges/badge-9.ico
--------------------------------------------------------------------------------
/src/images/systemtray/tray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/systemtray/tray.png
--------------------------------------------------------------------------------
/src/images/thumbar/darwin/mute.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/darwin/mute.png
--------------------------------------------------------------------------------
/src/images/thumbar/darwin/video.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/darwin/video.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/deafen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/deafen.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/mute.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/mute.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/video.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/video.png
--------------------------------------------------------------------------------
/src/images/systemtray/tray-muted.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/systemtray/tray-muted.png
--------------------------------------------------------------------------------
/src/images/systemtray/tray-unread.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/systemtray/tray-unread.png
--------------------------------------------------------------------------------
/src/images/thumbar/darwin/deafen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/darwin/deafen.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/mute-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/mute-off.png
--------------------------------------------------------------------------------
/src/images/systemtray/tray-connected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/systemtray/tray-connected.png
--------------------------------------------------------------------------------
/src/images/systemtray/tray-deafened.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/systemtray/tray-deafened.png
--------------------------------------------------------------------------------
/src/images/systemtray/tray-speaking.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/systemtray/tray-speaking.png
--------------------------------------------------------------------------------
/src/images/thumbar/darwin/deafen-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/darwin/deafen-off.png
--------------------------------------------------------------------------------
/src/images/thumbar/darwin/disconnect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/darwin/disconnect.png
--------------------------------------------------------------------------------
/src/images/thumbar/darwin/mute-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/darwin/mute-off.png
--------------------------------------------------------------------------------
/src/images/thumbar/darwin/video-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/darwin/video-off.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/deafen-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/deafen-off.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/disconnect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/disconnect.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/mute-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/mute-light.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/video-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/video-light.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/video-off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/video-off.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/deafen-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/deafen-light.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/deafen-off-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/deafen-off-light.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/disconnect-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/disconnect-light.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/mute-off-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/mute-off-light.png
--------------------------------------------------------------------------------
/src/images/thumbar/win32/video-off-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GooseMod/OpenCore/HEAD/src/images/thumbar/win32/video-off-light.png
--------------------------------------------------------------------------------
/src/bootstrapModules.js:
--------------------------------------------------------------------------------
1 | exports.init = (bootstrapModules) => {
2 | for (const mod of Object.keys(bootstrapModules)) {
3 | exports[mod] = bootstrapModules[mod];
4 | }
5 | };
--------------------------------------------------------------------------------
/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | echo "Packing asar..."
4 | asar pack src core.asar
5 |
6 | echo "Copying asar..."
7 | cp core.asar ~/.config/discordcanary/0.0.131/modules/discord_desktop_core/core.asar
8 |
9 | echo "Running discord..."
10 | echo ""
11 |
12 | discord-canary
--------------------------------------------------------------------------------
/src/ipcMain.js:
--------------------------------------------------------------------------------
1 | // This differs from app.asar's
2 | const { ipcMain } = require('electron');
3 |
4 | const ipcNamespace = 'DISCORD_';
5 | const getDiscordIPCEvent = (e) => e.startsWith(ipcNamespace) ? e : (ipcNamespace + e);
6 |
7 |
8 | module.exports = {
9 | on: (event, callback) => ipcMain.on(getDiscordIPCEvent(event), callback),
10 | removeListener: (event, callback) => ipcMain.removeListener(getDiscordIPCEvent(event), callback),
11 | reply: (event, channel, ...args) => event.sender.send(getDiscordIPCEvent(channel), ...args)
12 | };
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const bootstrapModules = require('./bootstrapModules');
2 |
3 | let mainScreen;
4 | exports.startup = (bootstrap) => {
5 | if (!bootstrap.securityUtils) { // Not using OpenAsar, additional isn't passed
6 | // Some warning here in future
7 | return;
8 | }
9 |
10 | Constants = require('./Constants');
11 | Constants.init(bootstrap.Constants);
12 |
13 | bootstrapModules.init(bootstrap);
14 |
15 | mainScreen = require('./mainScreen');
16 | mainScreen.makeWindow();
17 | };
18 |
19 | exports.setMainWindowVisible = (visible) => mainScreen.setVisible(visible);
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | const { readFileSync, writeFileSync } = require('fs');
2 | const { resolve, join } = require('path');
3 |
4 | const paths = require('./utils/paths');
5 |
6 | exports.exposeModuleResource = (asar, file) => {
7 | const asarPath = join(__dirname, asar, file);
8 |
9 | const nativePath = join(paths.getUserData(), file);
10 | writeFileSync(nativePath, readFileSync(asarPath));
11 |
12 | return nativePath;
13 | };
14 |
15 | const platform = process.platform;
16 | exports.platform = platform;
17 |
18 | exports.isWindows = platform.startsWith('win');
19 | exports.isOSX = platform === 'darwin';
20 | exports.isLinux = platform === 'linux';
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 GooseMod
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/images/close.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
--------------------------------------------------------------------------------
/src/Constants.js:
--------------------------------------------------------------------------------
1 | // Largely copied as nothing to do
2 | exports.init = (bootstrap) => {
3 | const { APP_NAME, API_ENDPOINT, NEW_UPDATE_ENDPOINT, UPDATE_ENDPOINT, APP_ID } = bootstrap; // From bootstrap consts
4 |
5 | const DEFAULT_MAIN_WINDOW_ID = 0;
6 | const MAIN_APP_DIRNAME = __dirname;
7 |
8 | const UpdaterEvents = {
9 | UPDATE_NOT_AVAILABLE: 'UPDATE_NOT_AVAILABLE',
10 | CHECKING_FOR_UPDATES: 'CHECKING_FOR_UPDATES',
11 | UPDATE_ERROR: 'UPDATE_ERROR',
12 | UPDATE_MANUALLY: 'UPDATE_MANUALLY',
13 | UPDATE_AVAILABLE: 'UPDATE_AVAILABLE',
14 | MODULE_INSTALL_PROGRESS: 'MODULE_INSTALL_PROGRESS',
15 | UPDATE_DOWNLOADED: 'UPDATE_DOWNLOADED',
16 | MODULE_INSTALLED: 'MODULE_INSTALLED',
17 | CHECK_FOR_UPDATES: 'CHECK_FOR_UPDATES',
18 | QUIT_AND_INSTALL: 'QUIT_AND_INSTALL',
19 | MODULE_INSTALL: 'MODULE_INSTALL',
20 | MODULE_QUERY: 'MODULE_QUERY',
21 | UPDATER_HISTORY_QUERY_AND_TRUNCATE: 'UPDATER_HISTORY_QUERY_AND_TRUNCATE',
22 | UPDATER_HISTORY_RESPONSE: 'UPDATER_HISTORY_RESPONSE'
23 | };
24 | const MenuEvents = {
25 | OPEN_HELP: 'menu:open-help',
26 | OPEN_SETTINGS: 'menu:open-settings',
27 | CHECK_FOR_UPDATES: 'menu:check-for-updates'
28 | };
29 |
30 | module.exports = {
31 | APP_NAME,
32 | DEFAULT_MAIN_WINDOW_ID,
33 | MAIN_APP_DIRNAME,
34 | APP_ID,
35 | API_ENDPOINT,
36 | NEW_UPDATE_ENDPOINT,
37 | UPDATE_ENDPOINT,
38 | UpdaterEvents,
39 | MenuEvents
40 | };
41 | };
--------------------------------------------------------------------------------
/src/appBadge.js:
--------------------------------------------------------------------------------
1 | const { BrowserWindow } = require('electron');
2 |
3 | const utils = require('./utils');
4 | const ipcMain = require('./ipcMain');
5 |
6 | let hasInit = false;
7 | let lastIndex = null;
8 | let appIcons = [];
9 |
10 | exports.init = () => {
11 | if (process.platform !== 'win32') { // Electron only supports win.setOverlayIcon on Windows
12 | log('AppBadge', 'Refusing to init due to non-Windows');
13 | return;
14 | }
15 |
16 | if (hasInit) {
17 | log('AppBadge', 'Already init, ignoring');
18 | return;
19 | }
20 |
21 | exports.hasInit = hasInit = true;
22 |
23 | for (let i = 1; i <= 11; i++) {
24 | appIcons.push(utils.exposeModuleResource('images/badges', `badge-${i}.ico`));
25 | }
26 |
27 | ipcMain.on('APP_BADGE_SET', (_e, num) => {
28 | const win = BrowserWindow.fromId(global.mainWindowId);
29 |
30 | let index, desc;
31 |
32 | switch (num) {
33 | case -1:
34 | index = 10;
35 | desc = 'Unread messages';
36 | break;
37 |
38 | case 0:
39 | index = null; // Clear icon
40 | desc = 'No notifications';
41 | break;
42 |
43 | default:
44 | index = Math.max(1, Math.min(num, 10)) - 1; // Clamp num between 1-10, -1 to make into
45 | desc = num + ' notifications';
46 | break;
47 | }
48 |
49 | if (lastIndex !== index) {
50 | lastIndex = index;
51 |
52 | if (index === null) return win.setOverlayIcon(null, desc);
53 |
54 | win.setOverlayIcon(appIcons[index], desc);
55 | }
56 | });
57 | };
--------------------------------------------------------------------------------
/src/images/discord.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
35 |
--------------------------------------------------------------------------------
/src/systemTray.js:
--------------------------------------------------------------------------------
1 | // Not modified *that* much as not much to change
2 | const { Tray, Menu, nativeImage } = require('electron');
3 |
4 | const ipcMain = require('./ipcMain');
5 | const securityUtils = require('./utils/securityUtils');
6 | const Constants = require('./Constants');
7 | const utils = require('./utils');
8 |
9 | const TrayIconNames = {
10 | DEFAULT: 'tray',
11 | UNREAD: 'tray-unread',
12 | CONNECTED: 'tray-connected',
13 | SPEAKING: 'tray-speaking',
14 | MUTED: 'tray-muted',
15 | DEAFENED: 'tray-deafened'
16 | };
17 | const MenuItems = {
18 | SECRET: 'SECRET',
19 | MUTE: 'MUTE',
20 | DEAFEN: 'DEAFEN',
21 | OPEN: 'OPEN',
22 | VOICE_SETTINGS: 'VOICE_SETTINGS',
23 | CHECK_UPDATE: 'CHECK_UPDATE',
24 | QUIT: 'QUIT',
25 | ACKNOWLEDGEMENTS: 'ACKNOWLEDGEMENTS'
26 | };
27 |
28 | let trayIcons = {};
29 | let menuItems = [];
30 | let applications = [];
31 | let contextMenu = [];
32 | let options, atomTray;
33 |
34 |
35 | exports.init = (_options) => {
36 | options = _options;
37 |
38 | const resourcePath = `images/systemtray`;
39 |
40 | for (const key of Object.keys(TrayIconNames)) {
41 | trayIcons[key] = utils.exposeModuleResource(resourcePath, `${TrayIconNames[key]}.png`);
42 | }
43 |
44 | currentIcon = trayIcons.DEFAULT;
45 |
46 | const {
47 | onToggleMute,
48 | onToggleDeafen,
49 | onTrayClicked,
50 | onOpenVoiceSettings,
51 | onCheckForUpdates
52 | } = options;
53 | const voiceConnected = currentIcon !== trayIcons.DEFAULT && currentIcon !== trayIcons.UNREAD;
54 |
55 | menuItems[MenuItems.SECRET] = {
56 | label: `Discord`,
57 | icon: trayIcons.DEFAULT,
58 | enabled: false
59 | };
60 | menuItems[MenuItems.MUTE] = {
61 | label: `Mute`,
62 | type: 'checkbox',
63 | checked: currentIcon === trayIcons.MUTED || currentIcon === trayIcons.DEAFENED,
64 | visible: voiceConnected,
65 | click: onToggleMute
66 | };
67 | menuItems[MenuItems.DEAFEN] = {
68 | label: `Deafen`,
69 | type: 'checkbox',
70 | checked: currentIcon === trayIcons.DEAFENED,
71 | visible: voiceConnected,
72 | click: onToggleDeafen
73 | };
74 | menuItems[MenuItems.OPEN] = {
75 | label: `Open ${Constants.APP_NAME}`,
76 | type: 'normal',
77 | visible: process.platform === 'linux',
78 | click: onTrayClicked
79 | };
80 | menuItems[MenuItems.VOICE_SETTINGS] = {
81 | label: 'Voice / Video Settings',
82 | type: 'normal',
83 | visible: voiceConnected,
84 | click: onOpenVoiceSettings
85 | };
86 | menuItems[MenuItems.CHECK_UPDATE] = {
87 | label: 'Check for Updates...',
88 | type: 'normal',
89 | visible: process.platform !== 'darwin',
90 | click: onCheckForUpdates
91 | };
92 | menuItems[MenuItems.QUIT] = {
93 | label: `Quit ${Constants.APP_NAME}`,
94 | role: 'quit'
95 | };
96 | menuItems[MenuItems.ACKNOWLEDGEMENTS] = {
97 | label: 'Acknowledgements',
98 | type: 'normal',
99 | visible: process.platform !== 'darwin',
100 | click: () => securityUtils.saferShellOpenExternal('https://discord.com/acknowledgements')
101 | };
102 |
103 | buildContext();
104 |
105 | ipcMain.on('SYSTEM_TRAY_SET_ICON', (_e, icon) => setTrayIcon(icon));
106 | ipcMain.on('SYSTEM_TRAY_SET_APPLICATIONS', (_e, newApplications) => setApplications(newApplications));
107 | };
108 |
109 | const buildContext = () => {
110 | const separator = {
111 | type: 'separator'
112 | };
113 |
114 | const hasApplications = applications != null && applications.length > 0;
115 | contextMenu = [menuItems[MenuItems.SECRET], separator, ...(hasApplications ? [...applications, separator] : []), menuItems[MenuItems.OPEN], menuItems[MenuItems.MUTE], menuItems[MenuItems.DEAFEN], menuItems[MenuItems.VOICE_SETTINGS], menuItems[MenuItems.CHECK_UPDATE], menuItems[MenuItems.ACKNOWLEDGEMENTS], separator, menuItems[MenuItems.QUIT]];
116 | };
117 |
118 | const launchApplication = (id) => options.onLaunchApplication(id);
119 |
120 | function setApplications(newApplications) {
121 | applications = newApplications.map(application => ({
122 | type: 'normal',
123 | label: application.name,
124 | click: () => launchApplication(application.id)
125 | }));
126 |
127 | buildContext();
128 | setContext();
129 | }
130 |
131 | const setContext = () => atomTray && atomTray.setContextMenu(Menu.buildFromTemplate(contextMenu));
132 |
133 | const show = () => {
134 | if (atomTray != null) return;
135 | atomTray = new Tray(nativeImage.createFromPath(currentIcon)); // Initialize with last set icon
136 |
137 | atomTray.setToolTip(Constants.APP_NAME); // Set tray context menu
138 | setContext(); // Set Tray click behavior
139 |
140 | atomTray.on('click', options.onTrayClicked);
141 | };
142 | exports.show = show;
143 |
144 | const hide = () => {
145 | if (atomTray == null) return;
146 |
147 | atomTray.destroy();
148 | atomTray = null;
149 | };
150 |
151 | const setTrayIcon = (icon) => {
152 | currentIcon = trayIcons[icon];
153 |
154 | if (icon == null) return hide();
155 | show();
156 |
157 | const muteIndex = contextMenu.indexOf(menuItems[MenuItems.MUTE]);
158 | const deafenIndex = contextMenu.indexOf(menuItems[MenuItems.DEAFEN]);
159 | const voiceConnected = contextMenu[muteIndex].visible;
160 | let shouldSetContextMenu = false;
161 |
162 | if (currentIcon !== trayIcons.DEFAULT && currentIcon !== trayIcons.UNREAD) {
163 | // Show mute/deaf controls
164 | if (!voiceConnected) {
165 | contextMenu[muteIndex].visible = true;
166 | contextMenu[deafenIndex].visible = true;
167 | shouldSetContextMenu = true;
168 | }
169 |
170 | if (currentIcon === trayIcons.DEAFENED) {
171 | contextMenu[muteIndex].checked = true;
172 | contextMenu[deafenIndex].checked = true;
173 | shouldSetContextMenu = true;
174 | } else if (currentIcon === trayIcons.MUTED) {
175 | contextMenu[muteIndex].checked = true;
176 | contextMenu[deafenIndex].checked = false;
177 | shouldSetContextMenu = true;
178 | } else if (contextMenu[muteIndex].checked || contextMenu[deafenIndex].checked) {
179 | contextMenu[muteIndex].checked = false;
180 | contextMenu[deafenIndex].checked = false;
181 | shouldSetContextMenu = true;
182 | }
183 | } else if (voiceConnected) {
184 | contextMenu[muteIndex].visible = false;
185 | contextMenu[deafenIndex].visible = false;
186 | shouldSetContextMenu = true;
187 | }
188 |
189 | shouldSetContextMenu && setContextMenu();
190 | return atomTray != null && atomTray.setImage(nativeImage.createFromPath(currentIcon));
191 | }
--------------------------------------------------------------------------------
/src/mainScreen.js:
--------------------------------------------------------------------------------
1 | const { BrowserWindow, screen, app } = require('electron');
2 |
3 | const appSettings = require('./appSettings');
4 | const buildInfo = require('./utils/buildInfo');
5 | const splashScreen = require('./splashScreen');
6 | const securityUtils = require('./utils/securityUtils');
7 | const systemTray = require('./systemTray');
8 | const appBadge = require('./appBadge');
9 |
10 | const settings = appSettings.getSettings();
11 |
12 | let mainWindow, mainWindowId;
13 |
14 |
15 | const MIN_WIDTH = settings.get('MIN_WIDTH', 940);
16 | const MIN_HEIGHT = settings.get('MIN_HEIGHT', 500);
17 | const MIN_VISIBLE_ON_SCREEN = 32;
18 | const DEFAULT_WIDTH = 1280;
19 | const DEFAULT_HEIGHT = 720;
20 | const DISCORD_NAMESPACE = 'DISCORD_';
21 |
22 | function doAABBsOverlap(a, b) { // Copied from Discord because a
23 | const ax1 = a.x + a.width;
24 | const bx1 = b.x + b.width;
25 | const ay1 = a.y + a.height;
26 | const by1 = b.y + b.height; // clamp a to b, see if it is non-empty
27 |
28 | const cx0 = a.x < b.x ? b.x : a.x;
29 | const cx1 = ax1 < bx1 ? ax1 : bx1;
30 |
31 | if (cx1 - cx0 > 0) {
32 | const cy0 = a.y < b.y ? b.y : a.y;
33 | const cy1 = ay1 < by1 ? ay1 : by1;
34 |
35 | if (cy1 - cy0 > 0) {
36 | return true;
37 | }
38 | }
39 |
40 | return false;
41 | }
42 |
43 | const genEndpoint = () => {
44 | const fromConfig = process.env.DISCORD_WEBAPP_ENDPOINT || settings.get('WEBAPP_ENDPOINT');
45 | if (fromConfig) return fromConfig;
46 |
47 | switch (buildInfo.releaseChannel) {
48 | case 'stable':
49 | return 'https://discord.com';
50 |
51 | case 'development':
52 | return 'https://canary.discord.com';
53 |
54 | default:
55 | return 'https://' + buildInfo.releaseChannel + '.discord.com';
56 | }
57 | };
58 |
59 | const WEBAPP_ENDPOINT = genEndpoint();
60 | const WEBAPP_PATH = settings.get('WEBAPP_PATH', `/app?_=${Date.now()}`);
61 | const URL_TO_LOAD = `${WEBAPP_ENDPOINT}${WEBAPP_PATH}`;
62 |
63 | exports.makeWindow = (isVisible = false) => {
64 | const windowOptions = {
65 | title: 'Discord',
66 | backgroundColor: settings.get('BACKGROUND_COLOR', '#2f3136'),
67 | width: DEFAULT_WIDTH,
68 | height: DEFAULT_HEIGHT,
69 | minWidth: MIN_WIDTH,
70 | minHeight: MIN_HEIGHT,
71 | transparent: false,
72 | frame: false,
73 | resizable: true,
74 | show: isVisible,
75 | webPreferences: {
76 | blinkFeatures: 'EnumerateDevices,AudioOutputDevices',
77 | nodeIntegration: false,
78 | // preload: _path.default.join(__dirname, 'mainScreenPreload.js'),
79 | nativeWindowOpen: true,
80 | enableRemoteModule: false,
81 | spellcheck: true,
82 | contextIsolation: true,
83 | // NB: this is required in order to give popouts (or any child window opened via window.open w/ nativeWindowOpen)
84 | // a chance at a node environment (i.e. they run the preload, have an isolated context, etc.) when
85 | // `app.allowRendererProcessReuse === false` (default in Electron 7).
86 | additionalArguments: ['--enable-node-leakage-in-renderers']
87 | }
88 | };
89 |
90 | const savedBounds = settings.get('WINDOW_BOUNDS');
91 | if (savedBounds) {
92 | savedBounds.width = Math.max(MIN_WIDTH, savedBounds.width);
93 | savedBounds.height = Math.max(MIN_HEIGHT, savedBounds.height);
94 |
95 | const displays = screen.getAllDisplays();
96 |
97 | const display = displays.find(display => {
98 | const displayBound = display.workArea;
99 |
100 | displayBound.x += MIN_VISIBLE_ON_SCREEN;
101 | displayBound.y += MIN_VISIBLE_ON_SCREEN;
102 | displayBound.width -= 2 * MIN_VISIBLE_ON_SCREEN;
103 | displayBound.height -= 2 * MIN_VISIBLE_ON_SCREEN;
104 |
105 | return doAABBsOverlap(savedBounds, displayBound);
106 | });
107 |
108 | if (display) {
109 | windowOptions.width = savedBounds.width;
110 | windowOptions.height = savedBounds.height;
111 | windowOptions.x = savedBounds.x;
112 | windowOptions.y = savedBounds.y;
113 | } else {
114 | windowOptions.center = true;
115 | }
116 | } else {
117 | windowOptions.center = true;
118 | }
119 |
120 | mainWindow = new BrowserWindow(windowOptions);
121 |
122 | mainWindowId = mainWindow.id;
123 | global.mainWindowId = mainWindowId;
124 |
125 | mainWindow.setMenuBarVisibility(false);
126 |
127 | // Event listeners oh no
128 | mainWindow.webContents.on('did-finish-load', () => {
129 | splashScreen.pageReady();
130 | });
131 |
132 | mainWindow.webContents.on('will-navigate', (e, url) => {
133 | if (securityUtils.checkUrlOriginMatches(url, WEBAPP_ENDPOINT)) e.preventDefault();
134 | });
135 |
136 | mainWindow.webContents.on('new-window', (e, url, name, _disposition, options) => {
137 | e.preventDefault();
138 |
139 | if (name.startsWith(DISCORD_NAMESPACE) && securityUtils.checkUrlOriginMatches(url, WEBAPP_ENDPOINT) && (new URL(url)).pathname === '/popout') {
140 | popoutWindows.openOrFocusWindow(e, url, name, options, WEBAPP_ENDPOINT);
141 | return;
142 | }
143 |
144 | securityUtils.saferShellOpenExternal(url).catch(err => {
145 | log('MainScreen', 'Error opening external url', url, err);
146 | });
147 | });
148 |
149 | systemTray.init({
150 | onCheckForUpdates: () => {
151 | const updater = _updater.default === null || _updater.default === void 0 ? void 0 : _updater.default.getUpdater();
152 |
153 | if (updater != null) {
154 | checkForUpdatesWithUpdater(updater);
155 | } else {
156 | legacyModuleUpdater.checkForUpdates();
157 | }
158 | },
159 |
160 | onTrayClicked: () => setWindowVisible(true, true),
161 |
162 | onOpenVoiceSettings: () => {}, // Stubs because no DiscordNative
163 | onToggleMute: () => {},
164 | onToggleDeafen: () => {},
165 | onLaunchApplication: () => {}
166 | });
167 |
168 | appBadge.init();
169 |
170 | if (process.platform === 'linux' || process.platform === 'win32') {
171 | systemTray.show();
172 |
173 | mainWindow.on('close', e => {
174 | if (mainWindow === null) { // Killed
175 | popoutWindows.closePopouts();
176 | return;
177 | }
178 |
179 | // saveWindowConfig(mainWindow); // Quit app if that's the setting
180 |
181 | if (!settings.get('MINIMIZE_TO_TRAY', true)) return app.quit(); // Quit if disabled to minimize to tray, else minimize
182 |
183 | setWindowVisible(false);
184 | e.preventDefault();
185 | });
186 | }
187 |
188 | mainWindow.loadURL(URL_TO_LOAD);
189 | };
190 |
191 | const setWindowVisible = (visible, unmin = false) => {
192 | if (!mainWindow) return;
193 |
194 | if (visible) {
195 | if (unmin || !mainWindow.isMinimized()) {
196 | mainWindow.show();
197 | }
198 | } else {
199 | mainWindow.hide();
200 | }
201 |
202 | mainWindow.setSkipTaskbar(!visible);
203 | };
204 |
205 | exports.setVisible = (visible) => setWindowVisible(visible);
--------------------------------------------------------------------------------