├── .gitignore ├── README.md ├── discord-chrome-app.crx ├── discord-hosted-app ├── README.md ├── discord-hosted-app.crx └── src │ ├── 128.png │ └── manifest.json ├── src ├── 128.png ├── css │ ├── reset.css │ └── style.css ├── html │ └── embed.html ├── js │ ├── background.js │ └── embed.js └── manifest.json └── updates.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.pem -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EoL 2 | ### The successor to this project can be found here: [Discord-PWA](https://github.com/NeverDecaf/discord-PWA) 3 | As of June 2022, [Chromium no longer supports Chrome Apps](https://blog.chromium.org/2020/08/changes-to-chrome-app-support-timeline.html). You can still use this app if you are using an older browser (or fork that still supports apps), but it will no longer be maintained. See [Discord-PWA](https://github.com/NeverDecaf/discord-PWA) for a replacement. 4 | 5 | # discord-chrome-app 6 | A chrome packaged app that mimics the desktop client while maintaining the privacy and security of the chrome browser sandbox. 7 | 8 | # Notice 9 | Chrome Apps have been deprecated for a while now and Google plans to [finally end support for them in 2021](https://blog.chromium.org/2020/01/moving-forward-from-chrome-apps.html). This will inevitably make this app unusable (unless you are willing to keep an old version of Chromium installed, which is a security risk). I am working on [an alternative](https://github.com/NeverDecaf/discord-PWA) in the form of a PWA. Currently it doesn't look as nice, but makes up for it with improved functionality. 10 | 11 | # Installation 12 | 1. Download [discord-chrome-app.crx](https://github.com/NeverDecaf/discord-chrome-app/releases/latest/download/discord-chrome-app.crx) from [releases](https://github.com/NeverDecaf/discord-chrome-app/releases) 13 | 2. Navigate to `chrome://extensions/` in your chromium based browser 14 | 3. Enable `Developer mode` (toggle/checkbox in top right corner, may vary depending on version) 15 | 4. Drag and drop the .crx file onto the `chrome://extensions/` page to install 16 | 5. To launch the app visit `chrome://apps/` 17 | 6. After lauching you can pin the app to your taskbar (on windows) and it will essentially function as a stand-alone program 18 | ### If drag-and-drop install fails, try this workaround: 19 | 1. Download the .crx from releases and extract the contents to a folder 20 | 2. Visit chrome://extensions/ and turn on developer mode (toggle in top right) 21 | 3. Click `Load unpacked` and select the directory you extracted the crx to. 22 | 23 | -------------------------------------------------------------------------------- /discord-chrome-app.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeverDecaf/discord-chrome-app/fb6fac878f2f530dd8b9b363cea99e91fa447ec2/discord-chrome-app.crx -------------------------------------------------------------------------------- /discord-hosted-app/README.md: -------------------------------------------------------------------------------- 1 | A hosted app version. This is basically a web page meaning it has access to all your extensions, etc. -------------------------------------------------------------------------------- /discord-hosted-app/discord-hosted-app.crx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeverDecaf/discord-chrome-app/fb6fac878f2f530dd8b9b363cea99e91fa447ec2/discord-hosted-app/discord-hosted-app.crx -------------------------------------------------------------------------------- /discord-hosted-app/src/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeverDecaf/discord-chrome-app/fb6fac878f2f530dd8b9b363cea99e91fa447ec2/discord-hosted-app/src/128.png -------------------------------------------------------------------------------- /discord-hosted-app/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Discord", 4 | "description": "Discord as hosted app", 5 | "version": "1.0", 6 | "icons": { 7 | "128": "128.png" 8 | }, 9 | "app": { 10 | "urls": [ 11 | "https://discordapp.com/" 12 | ], 13 | "launch": { 14 | "web_url": "https://discordapp.com/channels/@me", 15 | "container":"panel" 16 | } 17 | }, 18 | "permissions": [ 19 | "unlimitedStorage", 20 | "notifications" 21 | ] 22 | } -------------------------------------------------------------------------------- /src/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeverDecaf/discord-chrome-app/fb6fac878f2f530dd8b9b363cea99e91fa447ec2/src/128.png -------------------------------------------------------------------------------- /src/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /src/css/style.css: -------------------------------------------------------------------------------- 1 | .wordmark, 2 | .titleBar, 3 | .buttonClose,.buttonMin,.buttonMax { 4 | display:none; 5 | } 6 | 7 | html { 8 | background: #202225; 9 | } 10 | 11 | #webview { 12 | width: 100vw; 13 | position: absolute; 14 | height: 100vh; 15 | } 16 | 17 | svg[name="DiscordWordmark"] path { 18 | fill: rgb(114, 118, 125); 19 | } 20 | 21 | /* 22 | below styles can be made platform specific by adding the result of chrome.runtime.getPlatformInfo to the selector as a class 23 | for example: #webview.mac 24 | possible classes are: "mac", "win", "android", "cros", "linux", or "openbsd" 25 | title bar is currently hidden on android and chrome os 26 | */ 27 | #webview:not(.cros):not(.android) { 28 | top: 22px; 29 | height: calc(100vh - 22px); 30 | } 31 | 32 | .titleBar { 33 | display: inherit; 34 | width: 100%; 35 | height: 22px; 36 | position: absolute; 37 | background: #00000000; 38 | -webkit-app-region: drag; 39 | 40 | flex-shrink: 0; 41 | -webkit-box-orient: horizontal; 42 | -webkit-box-direction: reverse; 43 | flex-direction: row-reverse; 44 | -webkit-box-pack: start; 45 | -ms-flex-pack: start; 46 | justify-content: flex-start; 47 | display:flex; 48 | 49 | padding-top: 4px; 50 | opacity: 1; 51 | 52 | -moz-transition: background .2s ease-in; 53 | -o-transition: background .2s ease-in; 54 | -webkit-transition: background .2s ease-in; 55 | transition: background .2s ease-in; 56 | } 57 | 58 | #backdrop { 59 | width: 100%; 60 | height: 22px; 61 | position: absolute; 62 | background: #000000; 63 | z-index: -1; 64 | opacity: 0; 65 | } 66 | 67 | #barbackground { 68 | width: 100%; 69 | height: 22px; 70 | position: absolute; 71 | background: #202225; 72 | z-index: -2; 73 | } 74 | 75 | .wordmark { 76 | display: block; 77 | position:absolute; 78 | top: 0; 79 | left: 0; 80 | opacity: 1; 81 | padding: 4px 9px 3px 9px; 82 | } 83 | 84 | .buttonClose,.buttonMin,.buttonMax { 85 | display: flex; 86 | 87 | position: relative; 88 | top: -4px; 89 | width: 28px; 90 | height: 22px; 91 | cursor: pointer; 92 | opacity: .6; 93 | -webkit-app-region: no-drag; 94 | -webkit-box-direction: reverse; 95 | -webkit-box-align: center; 96 | 97 | display:flex; 98 | 99 | -webkit-box-align: center; 100 | -ms-flex-align: center; 101 | align-items: center; 102 | justify-content: center; 103 | 104 | -webkit-box-pack: center; 105 | -ms-flex-pack: center; 106 | 107 | margin: 0; 108 | padding: 0; 109 | border: 0; 110 | 111 | vertical-align: baseline; 112 | 113 | line-height:16px; 114 | pointer-events:auto; 115 | user-select:none; 116 | } 117 | .buttonClose:hover { 118 | background-color: #f04747; 119 | opacity:1; 120 | } 121 | .buttonMin:hover,.buttonMax:hover { 122 | background-color: rgba(79, 84, 92, 0.16); 123 | opacity:1; 124 | } -------------------------------------------------------------------------------- /src/html/embed.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Discord 6 | 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/js/background.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Listens for the app launching then creates the window 3 | * 4 | * @see http://developer.chrome.com/apps/app.runtime.html 5 | * @see http://developer.chrome.com/apps/app.window.html 6 | */ 7 | chrome.app.runtime.onLaunched.addListener(function () { 8 | runApp(); 9 | }); 10 | 11 | /** 12 | * Listens for the app restarting then re-creates the window. 13 | * 14 | * @see http://developer.chrome.com/apps/app.runtime.html 15 | */ 16 | chrome.app.runtime.onRestarted.addListener(function () { 17 | runApp(); 18 | }); 19 | 20 | /** 21 | * Creates the window for the application. 22 | * 23 | * @see http://developer.chrome.com/apps/app.window.html 24 | */ 25 | function runApp() { 26 | chromeAppWindow = {"id":"embed","frame":{"type":"none"},"innerBounds":{"width":1180,"height":900}} 27 | chrome.app.window.create('html/embed.html', chromeAppWindow, onWindowLoaded); 28 | } 29 | 30 | /** 31 | * Called before the contentWindow's onload event 32 | * 33 | * @see http://developer.chrome.com/apps/app.window.html 34 | */ 35 | onWindowLoaded = function (win) { 36 | 37 | function newWindow_openInTabAndInterceptRedirect(newWindow) { 38 | // Create an invisible proxy webview to listen to redirect 39 | // requests from |newWindow| (the window that the guest is 40 | // trying to open). NOTE: The proxy webview currently has to 41 | // live somewhere in the DOM, so we append it to the body. 42 | // This requirement is in the process of being eliminated. 43 | var proxyWebview = document.createElement('webview'); 44 | document.body.appendChild(proxyWebview); 45 | 46 | // Listen to onBeforeRequest event (chrome.webRequest API) 47 | // on proxyWebview in order to intercept newWindow's redirects. 48 | var onBeforeRequestListener = function(e) { 49 | // Only consider top-level non-blank redirects. 50 | if (e.type === "main_frame" && e.url.split('#')[0] !== 'about:blank') { 51 | win.contentWindow.open(e.url); 52 | // Don't need proxyWebview anymore. 53 | document.body.removeChild(proxyWebview); 54 | // Handled this redirect: cancel further processing. 55 | return { cancel: true }; 56 | } else { 57 | // Ignored this redirect: proceed with default processing. 58 | return { cancel: false }; 59 | } 60 | }; 61 | proxyWebview.request.onBeforeRequest.addListener( 62 | onBeforeRequestListener, 63 | { urls: [ "*://*/*" ] }, 64 | [ 'blocking' ] 65 | ); 66 | 67 | // Attach |newWindow| to proxyWebview. From the original 68 | // webview guest's point of view, the window is now opened 69 | // and ready to be redirected: when it does so, the redirect 70 | // will be intercepted by |onBeforeRequestListener|. 71 | newWindow.attach(proxyWebview); 72 | } 73 | 74 | 75 | 76 | win.contentWindow.onload = function () { 77 | var webview = win.contentWindow.document.getElementById('webview'); 78 | webview.addEventListener('permissionrequest', function (e) { 79 | e.request.allow(); 80 | }); 81 | webview.addEventListener('newwindow', function (e) { 82 | e.preventDefault(); 83 | if (e.targetUrl.split('#')[0] !== 'about:blank') 84 | { 85 | win.contentWindow.open(e.targetUrl); 86 | } 87 | else 88 | { 89 | newWindow_openInTabAndInterceptRedirect(e.window); 90 | } 91 | }); 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /src/js/embed.js: -------------------------------------------------------------------------------- 1 | var webview = document.getElementById('webview'); 2 | var curwindow = chrome.app.window.current(); 3 | document.getElementById("buttonClose").addEventListener("click", function() { 4 | curwindow.close(); 5 | }); 6 | document.getElementById("buttonMax").addEventListener("click", function() { 7 | if (curwindow.isMaximized()) 8 | curwindow.restore(); 9 | else 10 | curwindow.maximize(); 11 | }); 12 | document.getElementById("buttonMin").addEventListener("click", function() { 13 | curwindow.minimize(); 14 | }); 15 | /* hide "Download Apps" item */ 16 | csstoinject=` 17 | div[class|=scroller][dir="ltr"] > div[class|=listItem]:nth-last-of-type(2), 18 | div[class|=scroller][dir="ltr"] > div[class|=listItem]:nth-last-of-type(3) { 19 | display:none 20 | } 21 | `; 22 | chrome.runtime.getPlatformInfo(function (info) { 23 | var os = undefined; 24 | if (info != undefined) { 25 | var decos = document.getElementsByClassName("os-decoration"); for (var i=0; i div[class|=container] > nav[class*= guilds] + div[class*= base] { 37 | border-radius: 8px 0 0 8px 38 | } 39 | nav[class^=wrapper] div[class*= scroller]:not([class*= scrollerWrap]) { 40 | padding-top: 4px 41 | } 42 | `; 43 | break; 44 | } 45 | }); 46 | 47 | webview.addEventListener('contentload', function () { 48 | 49 | webview.contentWindow.postMessage('init', 'https://discord.com/*'); 50 | window.addEventListener('message', function(e) { 51 | if (e.data == 'drawAttention') 52 | { 53 | curwindow.drawAttention(); 54 | } 55 | else if (e.data == 'clearAttention') 56 | { 57 | // clearAttention seems broken. it doesn't actually clear anything and after calling it drawAttention no longer does anything. 58 | // curwindow.clearAttention(); 59 | } 60 | else 61 | { 62 | document.getElementById('backdrop').style.opacity = e.data; 63 | } 64 | }); 65 | webview.executeScript({ 66 | code: ` 67 | var hasNewMessages = 0; 68 | var node = document.createElement('style'); 69 | node.innerHTML = \`` + csstoinject + `\`; 70 | document.body.appendChild(node); 71 | 72 | var messageSource, messageOrigin; 73 | addEventListener('message', function hello(e) { 74 | if (!messageSource) { 75 | if (e.data == "init") { 76 | removeEventListener('message',hello,false); 77 | const messageSource = e.source; 78 | const messageOrigin = e.origin; 79 | 80 | focuslost = function(e) { 81 | if (hasNewMessages && messageSource) 82 | messageSource.postMessage('drawAttention', messageOrigin); 83 | } 84 | window.onblur = focuslost; 85 | window.onfocusout = focuslost; 86 | var observer = new MutationObserver(function(mutations) { 87 | mutations.forEach(function(mutation) { 88 | if ( mutation.target.href != 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADxElEQVRYR+1XS0wTaxT+ZoaWabVUQN6Uh4XY+ojv5C50YUJu7uIaY4wujGI06EqjIo2Jj5WPGAsYjRu8JsbHSu8z92rie2GMMVEjC2kJVKBY5FlTCp0+mBnzj3bo0NIO2qsuPJu285//nO//zjnfP6XwyTbdEJkyt/MIROwDkBN9nuZPLyicc5dZTt7cTPEkNhVNcLDReRSieDzNCROHo6hjTQ2WE0oAdscwgNyvAgAYabJZ504FIH6l5FKaJptVYn+yBHbHDwDTMlCcl4kVC42YY9DAMxhEq3MUOpZBppaW6AyGBYQjAtYsz0ZxHouedxxevPahbyg0bVVVlcCgz8CGmgIU5GrRPxyWEpYXsVLyROYfn0CXhwObSaMkn0WnO4C/HwzAH5iIc1cFYOuvxVhcbZA2M4zcLqp6NRIRoNHQeOUcxfX/+mYOwGjIwJHdVaBnljcukSACJy92wudXspCSgV9W56Hmp/TIwr2nw7jzhMjMpKUEcHiXGTlGjSq6Uzl5fRGc+s2lHgDp+vrtlXFxQ2EBbweCKC1g5QmIOo0FeAx6Qygr0iEjQb80X+lSTEVSBtasyMH6tfkKAFyQR/PVbrwfjSA7S4P62gp5Gobeh3H+eg+4EA9TIYs9W8rBTGmefx4N4vELrxwzKQDS/UstWQoAre1+XPvXIz/btq4ES+Z/nJCHz0Zw+/GQvLZ/W4XEUqxNnYakAAj9pAyxRk7ZeLkLvCBKp2vYUYm8bK3k0uEeR8uNXum7XsfgcJ1Z0oJYI6JEyhC1pACO76lOKDY9fRzaXGNYYJ6N8mKdIoHjzRjI+jJrFgpyleCJIynhsQsd6gDYGyzyLUX0meN46WQzMdIPWg0t9wKJY2t0qgNg21GpOAWhj5ywvEiHeSZ9UnFyv+Pg6g2gslSPihiWBkZCsF9WWQLDrAyQRjSb9DJiomgv23z46/4ASgtZ5OdooWcZ0DSFcY6H1xdGb38QRMBWLTIqRpEAInJM7gpVPUCciAJbzbOxcqFROgkpwaU/eqULJplVlelRt9GEQJBHt4fD89c+OFxjmHrVplTCmdT7c3x/APguGSDdpVSXzynu5J5+AMbpYsYx0GB3XBGB2i/LqdjtF2lhAyXQfwJQXiyJXssPnXYZJ5jw7wBq0gWCAtYKtOCjBPouAOmPyLQ6EF042NhuoUShMB0geIbuOFs/32M701YtUPQDQDSlBJCOxIliHGhuL6F54RaAJWQ9rgf+r8Sxcfee78jShCZuUsDP3wQAAbO75bnGMDqrpclm3Ul+fwCF65kwy+AsFwAAAABJRU5ErkJggg==' ) 89 | { 90 | messageSource.postMessage('drawAttention', messageOrigin); 91 | hasNewMessages = 1; 92 | } 93 | else 94 | { 95 | messageSource.postMessage('clearAttention', messageOrigin); 96 | hasNewMessages = 0; 97 | } 98 | }); 99 | }); 100 | observer.observe(document.querySelector("link[rel|='icon']"), { 101 | childList: false, 102 | subtree: false, 103 | attributes: true, 104 | characterData: false, 105 | }); 106 | var modal_observer = new MutationObserver(function(mutations) { 107 | mutations.forEach(function(mutation) { 108 | mutation.target.classList.forEach(function (cv, ci, lo) { 109 | if (cv.startsWith('backdrop')) 110 | messageSource.postMessage(mutation.target.style.opacity, messageOrigin); 111 | }); 112 | }); 113 | }); 114 | modal_observer.observe(document.querySelector("div[data-no-focus-lock]"), { 115 | childList: false, 116 | subtree: true, 117 | attributes: true, 118 | characterData: false, 119 | }); 120 | } 121 | } 122 | });`}); 123 | }); 124 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | {"manifest_version":2,"name":"Discord","description":"Desktop App Clone","version":"1.2.5.1","icons":{"128":"128.png"},"app":{"background":{"scripts":["js/background.js"],"pages":["html/embed.html"]}},"permissions":["webview","fullscreen","notifications","audioCapture","videoCapture"],"update_url": "https://raw.githubusercontent.com/NeverDecaf/discord-chrome-app/master/updates.xml"} -------------------------------------------------------------------------------- /updates.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | --------------------------------------------------------------------------------