mirror of
				https://github.com/smartfrigde/armcord.git
				synced 2024-08-14 23:56:58 +00:00 
			
		
		
		
	Make invite socket use arrpc
This commit is contained in:
		
							parent
							
								
									d7a318d5c3
								
							
						
					
					
						commit
						e6df2b0ecf
					
				
					 4 changed files with 17 additions and 199 deletions
				
			
		|  | @ -11,13 +11,8 @@ async function run() { | |||
|     server.on("activity", (data) => mainWindow.webContents.send("rpc", data)); | ||||
|     server.on("invite", (code) => { | ||||
|         console.log(code); | ||||
|         const {createInviteWindow, inviteWindow} = require("../../../ts-out/window.js"); | ||||
|         const {exportPort} = require("./transports/websocket.js"); | ||||
|         createInviteWindow(); | ||||
|         const win = inviteWindow; | ||||
|         //doesnt work
 | ||||
|         win.loadURL("https://discord.com/invite/" + code); | ||||
|         win.show(); | ||||
|         const {createInviteWindow} = require("../../../ts-out/window.js"); | ||||
|         createInviteWindow(code); | ||||
|     }); | ||||
| } | ||||
| run(); | ||||
|  |  | |||
							
								
								
									
										186
									
								
								src/socket.ts
									
										
									
									
									
								
							
							
						
						
									
										186
									
								
								src/socket.ts
									
										
									
									
									
								
							|  | @ -1,186 +0,0 @@ | |||
| import type {Server, WebSocket} from "ws"; | ||||
| import {inviteWindow, createInviteWindow} from "./window"; | ||||
| /* | ||||
|  MIT License | ||||
| 
 | ||||
|  Copyright (c) 2020-2022 Dawid Papiewski "SpacingBat3" | ||||
| 
 | ||||
|  Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  of this software and associated documentation files (the "Software"), to deal | ||||
|  in the Software without restriction, including without limitation the rights | ||||
|  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  copies of the Software, and to permit persons to whom the Software is | ||||
|  furnished to do so, subject to the following conditions: | ||||
| 
 | ||||
|  The above copyright notice and this permission notice shall be included in all | ||||
|  copies or substantial portions of the Software. | ||||
| 
 | ||||
|  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  SOFTWARE. | ||||
|  */ | ||||
| async function wsLog(message: string, ...args: unknown[]) { | ||||
|     console.log("[WebSocket] " + message, ...args); | ||||
| } | ||||
| 
 | ||||
| /** Generates an inclusive range (as `Array`) from `start` to `end`. */ | ||||
| function range(start: number, end: number) { | ||||
|     return Array.from({length: end - start + 1}, (_v, k) => start + k); | ||||
| } | ||||
| 
 | ||||
| interface InviteResponse { | ||||
|     /** Response type/command. */ | ||||
|     cmd: "INVITE_BROWSER"; | ||||
|     /** Response arguments. */ | ||||
|     args: { | ||||
|         /** An invitation code. */ | ||||
|         code: string; | ||||
|     }; | ||||
|     /** Nonce indentifying the communication. */ | ||||
|     nonce: string; | ||||
| } | ||||
| 
 | ||||
| function isInviteResponse(data: unknown): data is InviteResponse { | ||||
|     if (!(data instanceof Object)) return false; | ||||
|     if ((data as Partial<InviteResponse>)?.cmd !== "INVITE_BROWSER") return false; | ||||
|     if (typeof (data as Partial<InviteResponse>)?.args?.code !== "string") return false; | ||||
|     if (typeof (data as Partial<InviteResponse>)?.nonce !== "string") return false; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| const messages = { | ||||
|     /** | ||||
|      * A fake, hard-coded Discord command to spoof the presence of | ||||
|      * official Discord client (which makes browser to actually start a | ||||
|      * communication with the ArmCord). | ||||
|      */ | ||||
|     handShake: { | ||||
|         /** Message command. */ | ||||
|         cmd: "DISPATCH", | ||||
|         /** Message data. */ | ||||
|         data: { | ||||
|             /** Message scheme version. */ | ||||
|             v: 1, | ||||
|             /** Client properties. */ | ||||
|             config: { | ||||
|                 /** Discord CDN host (hard-coded for `dicscord.com` instance). */ | ||||
|                 cdn_host: "cdn.discordapp.com", | ||||
|                 /** API endpoint (hard-coded for `dicscord.com` instance). */ | ||||
|                 api_endpoint: "//discord.com/api", | ||||
|                 /** Client type. Can be (probably) `production` or `canary`. */ | ||||
|                 environment: "production" | ||||
|             } | ||||
|         }, | ||||
|         evt: "READY", | ||||
|         nonce: null | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Tries to reserve the server at given port. | ||||
|  * | ||||
|  * @returns `Promise`, which always resolves (either to `Server<WebSocket>` on | ||||
|  *          success or `null` on failure). | ||||
|  */ | ||||
| async function getServer(port: number) { | ||||
|     const {WebSocketServer} = await import("ws"); | ||||
|     return new Promise<Server<WebSocket> | null>((resolve) => { | ||||
|         const wss = new WebSocketServer({host: "127.0.0.1", port}); | ||||
|         wss.once("listening", () => resolve(wss)); | ||||
|         wss.once("error", () => resolve(null)); | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Tries to start a WebSocket server at given port range. If it suceed, it will | ||||
|  * listen to the browser requests which are meant to be sent to official | ||||
|  * Discord client. | ||||
|  * | ||||
|  * Currently it supports only the invitation link requests. | ||||
|  * | ||||
|  */ | ||||
| export default async function startServer() { | ||||
|     function isJsonSyntaxCorrect(string: string) { | ||||
|         try { | ||||
|             JSON.parse(string); | ||||
|         } catch { | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|     /** Known Discord instances, including the official ones. */ | ||||
|     const knownInstancesList = [ | ||||
|         ["Discord", new URL("https://discord.com/app")], | ||||
|         ["Discord Canary", new URL("https://canary.discord.com/app")], | ||||
|         ["Discord PTB", new URL("https://ptb.discord.com/app")], | ||||
|         ["Fosscord", new URL("https://dev.fosscord.com/app")] | ||||
|     ] as const; | ||||
| 
 | ||||
|     let wss = null, | ||||
|         wsPort = 6463; | ||||
|     for (const port of range(6463, 6472)) { | ||||
|         wss = await getServer(port); | ||||
|         if (wss !== null) { | ||||
|             void wsLog("ArmCord is listening at " + port.toString()); | ||||
|             wsPort = port; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     if (wss === null) return; | ||||
|     let lock = false; | ||||
|     wss.on("connection", (wss, request) => { | ||||
|         const origin = request.headers.origin ?? "https://discord.com"; | ||||
|         let known = false; | ||||
|         for (const instance of knownInstancesList) { | ||||
|             if (instance[1].origin === origin) known = true; | ||||
|         } | ||||
|         if (!known) return; | ||||
|         wss.send(JSON.stringify(messages.handShake)); | ||||
|         wss.once("message", (data, isBinary) => { | ||||
|             if (lock) return; | ||||
|             lock = true; | ||||
|             let parsedData: unknown = data; | ||||
|             if (!isBinary) parsedData = data.toString(); | ||||
|             if (isJsonSyntaxCorrect(parsedData as string)) parsedData = JSON.parse(parsedData as string); | ||||
|             if (isInviteResponse(parsedData)) { | ||||
|                 // Replies to browser, so it finds the communication successful.
 | ||||
|                 wss.send( | ||||
|                     JSON.stringify({ | ||||
|                         cmd: parsedData.cmd, | ||||
|                         data: { | ||||
|                             invite: null, | ||||
|                             code: parsedData.args.code | ||||
|                         }, | ||||
|                         evt: null, | ||||
|                         nonce: parsedData.nonce | ||||
|                     }) | ||||
|                 ); | ||||
|                 createInviteWindow(); | ||||
|                 const child = inviteWindow; | ||||
|                 if (child === undefined) return; | ||||
|                 void child.loadURL(origin + "/invite/" + parsedData.args.code); | ||||
|                 child.webContents.once("did-finish-load", () => { | ||||
|                     child.show(); | ||||
|                 }); | ||||
|                 child.webContents.once("will-navigate", () => { | ||||
|                     lock = false; | ||||
|                     child.close(); | ||||
|                 }); | ||||
|                 child.on("close", (e) => { | ||||
|                     lock = false; | ||||
|                 }); | ||||
|                 // Blocks requests to ArmCord's WS, to prevent loops.
 | ||||
|                 child.webContents.session.webRequest.onBeforeRequest( | ||||
|                     { | ||||
|                         urls: ["ws://127.0.0.1:" + wsPort.toString() + "/*"] | ||||
|                     }, | ||||
|                     (_details, callback) => callback({cancel: true}) | ||||
|                 ); | ||||
|             } | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  | @ -1,6 +1,6 @@ | |||
| import * as fs from "fs"; | ||||
| import {app, Menu, Tray, nativeImage} from "electron"; | ||||
| import {mainWindow} from "./window"; | ||||
| import {createInviteWindow, mainWindow} from "./window"; | ||||
| import {getConfig, getConfigLocation, setWindowState, getDisplayVersion} from "./utils"; | ||||
| import * as path from "path"; | ||||
| import {createSettingsWindow} from "./settings/main"; | ||||
|  | @ -113,8 +113,7 @@ app.whenReady().then(async () => { | |||
|                 { | ||||
|                     label: "Support Discord Server", | ||||
|                     click: function () { | ||||
|                         mainWindow.show(); | ||||
|                         mainWindow.loadURL("https://discord.gg/TnhxcqynZ2"); | ||||
|                         createInviteWindow("TnhxcqynZ2"); | ||||
|                     } | ||||
|                 }, | ||||
|                 { | ||||
|  |  | |||
|  | @ -17,7 +17,6 @@ import { | |||
| import {registerIpc} from "./ipc"; | ||||
| import {setMenu} from "./menu"; | ||||
| import * as fs from "fs"; | ||||
| import startServer from "./socket"; | ||||
| import contextMenu from "electron-context-menu"; | ||||
| import os from "os"; | ||||
| import {tray} from "./tray"; | ||||
|  | @ -292,7 +291,7 @@ export function createTransparentWindow() { | |||
|     }); | ||||
|     doAfterDefiningTheWindow(); | ||||
| } | ||||
| export function createInviteWindow() { | ||||
| export function createInviteWindow(code: string) { | ||||
|     inviteWindow = new BrowserWindow({ | ||||
|         width: 800, | ||||
|         height: 600, | ||||
|  | @ -306,5 +305,16 @@ export function createInviteWindow() { | |||
|             spellcheck: true | ||||
|         } | ||||
|     }); | ||||
|     inviteWindow.hide(); | ||||
|     var formInviteURL = `https://discord.com/invite/${code}`; | ||||
|     inviteWindow.webContents.session.webRequest.onBeforeRequest((details, callback) => { | ||||
|         if (details.url.includes("ws://")) return callback({cancel: true}); | ||||
|         return callback({}); | ||||
|     }); | ||||
|     inviteWindow.loadURL(formInviteURL); | ||||
|     inviteWindow.webContents.once("did-finish-load", () => { | ||||
|         inviteWindow.show(); | ||||
|         inviteWindow.webContents.once("will-navigate", () => { | ||||
|             inviteWindow.close(); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue