mirror of
				https://github.com/smartfrigde/armcord.git
				synced 2024-08-14 23:56:58 +00:00 
			
		
		
		
	Add experimental arrpc
This commit is contained in:
		
							parent
							
								
									0bd294480d
								
							
						
					
					
						commit
						a0e0f6a516
					
				
					 16 changed files with 813 additions and 3 deletions
				
			
		
							
								
								
									
										2
									
								
								src/arrpc/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/arrpc/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | |||
| node_modules | ||||
| package-lock.json | ||||
							
								
								
									
										21
									
								
								src/arrpc/LICENSE
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/arrpc/LICENSE
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| MIT License | ||||
| 
 | ||||
| Copyright (c) 2022 OpenAsar | ||||
| 
 | ||||
| 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. | ||||
							
								
								
									
										44
									
								
								src/arrpc/README.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/arrpc/README.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| # arRPC | ||||
| 
 | ||||
| arRPC is an open source implementation of Discord's half-documented local RPC servers for their desktop client. This open source implementation purely in NodeJS allows it to be used in many places where it is otherwise impossible to do: Discord web and alternative clients like Armcord/etc. It opens a simple bridge WebSocket server which messages the JSON of exactly what to dispatch with in the client with no extra processing needed, allowing small and simple mods or plugins. **It is currently in alpha and is very WIP, expect bugs, etc.** | ||||
| 
 | ||||
| ### How to try | ||||
| 
 | ||||
| 1. Clone repo | ||||
| 2. Run server with `node src` (use new Node) | ||||
| 3. Open Discord in browser with CSP disabled (using an extension) | ||||
| 4. Run content of [`simple_mod.js`](simple_mod.js) in console | ||||
| 5. Use an app/thing with RPC | ||||
| 6. Hope it works, if not report bugs :) | ||||
| 
 | ||||
| ## Supported | ||||
| 
 | ||||
| ### Transports | ||||
| 
 | ||||
| -   [x] WebSocket Server | ||||
|     -   [x] JSON | ||||
|     -   [ ] Erlpack | ||||
| -   [ ] HTTP Server | ||||
| -   [x] IPC | ||||
| 
 | ||||
| ### Commands | ||||
| 
 | ||||
| -   [x] DISPATCH | ||||
| -   [ ] AUTHORIZE | ||||
| -   [ ] AUTHENTICATE | ||||
| -   [ ] GET_GUILD | ||||
| -   [ ] GET_GUILDS | ||||
| -   [ ] GET_CHANNEL | ||||
| -   [ ] GET_CHANNELS | ||||
| -   [ ] SUBSCRIBE | ||||
| -   [ ] UNSUBSCRIBE | ||||
| -   [ ] SET_USER_VOICE_SETTINGS | ||||
| -   [ ] SELECT_VOICE_CHANNEL | ||||
| -   [ ] GET_SELECTED_VOICE_CHANNEL | ||||
| -   [ ] SELECT_TEXT_CHANNEL | ||||
| -   [ ] GET_VOICE_SETTINGS | ||||
| -   [ ] SET_VOICE_SETTINGS | ||||
| -   [ ] SET_CERTIFIED_DEVICES | ||||
| -   [x] SET_ACTIVITY | ||||
| -   [ ] SEND_ACTIVITY_JOIN_INVITE | ||||
| -   [ ] CLOSE_ACTIVITY_REQUEST | ||||
							
								
								
									
										22
									
								
								src/arrpc/package.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/arrpc/package.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| { | ||||
|     "name": "arrpc", | ||||
|     "version": "0.1.0", | ||||
|     "description": "Open Discord RPC server for atypical setups", | ||||
|     "main": "src/index.js", | ||||
|     "scripts": { | ||||
|         "test": "echo \"Error: no test specified\" && exit 1" | ||||
|     }, | ||||
|     "repository": { | ||||
|         "type": "git", | ||||
|         "url": "git+https://github.com/OpenAsar/arrpc.git" | ||||
|     }, | ||||
|     "author": "OpenAsar", | ||||
|     "license": "MIT", | ||||
|     "bugs": { | ||||
|         "url": "https://github.com/OpenAsar/arrpc/issues" | ||||
|     }, | ||||
|     "homepage": "https://github.com/OpenAsar/arrpc#readme", | ||||
|     "dependencies": { | ||||
|         "ws": "^8.11.0" | ||||
|     } | ||||
| } | ||||
							
								
								
									
										31
									
								
								src/arrpc/simple_mod.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/arrpc/simple_mod.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| const dispatch = (() => { | ||||
|     let Dispatcher; | ||||
| 
 | ||||
|     return function (event) { | ||||
|         Dispatcher ??= window.Vencord?.Webpack.Common.FluxDispatcher; | ||||
|         if (!Dispatcher) { | ||||
|             const cache = webpackChunkdiscord_app.push([[Symbol()], {}, (w) => w]).c; | ||||
|             webpackChunkdiscord_app.pop(); | ||||
| 
 | ||||
|             outer: for (const id in cache) { | ||||
|                 const mod = cache[id].exports; | ||||
|                 for (const exp in mod) { | ||||
|                     if (mod[exp]?.isDispatching) { | ||||
|                         Dispatcher = mod[exp]; | ||||
|                         break outer; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (!Dispatcher) return; // failed to find, your choice if and how u wanna handle this
 | ||||
| 
 | ||||
|         return Dispatcher.dispatch(event); | ||||
|     }; | ||||
| })(); | ||||
| const ws = new WebSocket("ws://localhost:1337"); // connect to arRPC bridge
 | ||||
| ws.onmessage = (x) => { | ||||
|     msg = JSON.parse(x.data); | ||||
|     console.log(msg); | ||||
| 
 | ||||
|     dispatch({type: "LOCAL_ACTIVITY_UPDATE", ...msg}); // set RPC status
 | ||||
| }; | ||||
							
								
								
									
										8
									
								
								src/arrpc/src/bridge.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/arrpc/src/bridge.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| const ws = require("ws"); | ||||
| const send = (msg) => { | ||||
|     wss.clients.forEach((x) => x.send(JSON.stringify(msg))); | ||||
| }; | ||||
| 
 | ||||
| const wss = new ws.WebSocketServer({port: 1337}); | ||||
| 
 | ||||
| module.exports = {send}; | ||||
							
								
								
									
										11
									
								
								src/arrpc/src/index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/arrpc/src/index.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| var __importDefault = | ||||
|     (this && this.__importDefault) || | ||||
|     function (mod) { | ||||
|         return mod && mod.__esModule ? mod : {default: mod}; | ||||
|     }; | ||||
| const server = require("./server.js"); | ||||
| global.fetch = __importDefault(require("node-fetch")); | ||||
| async function start() { | ||||
|     const x = await new server.RPCServer(); | ||||
| } | ||||
| start(); | ||||
							
								
								
									
										82
									
								
								src/arrpc/src/server.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/arrpc/src/server.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,82 @@ | |||
| const rgb = (r, g, b, msg) => `\x1b[38;2;${r};${g};${b}m${msg}\x1b[0m`; | ||||
| const log = (...args) => console.log(`[${rgb(88, 101, 242, "arRPC")} > ${rgb(87, 242, 135, "bridge")}]`, ...args); | ||||
| 
 | ||||
| const IPCServer = require("./transports/ipc.js"); | ||||
| 
 | ||||
| const WSServer = require("./transports/websocket.js"); | ||||
| const Bridge = require("./bridge.js"); | ||||
| 
 | ||||
| const lookupAsset = (name, assets) => { | ||||
|     return assets.find((x) => x.name === name)?.id; | ||||
| }; | ||||
| class RPCServer { | ||||
|     constructor() { | ||||
|         return (async () => { | ||||
|             this.onConnection = this.onConnection.bind(this); | ||||
|             this.onMessage = this.onMessage.bind(this); | ||||
| 
 | ||||
|             this.ipc = await new IPCServer.IPCServer(this.onMessage, this.onConnection); | ||||
|             this.ws = await new WSServer.WSServer(this.onMessage, this.onConnection); | ||||
|         })(); | ||||
|     } | ||||
| 
 | ||||
|     onConnection(socket) { | ||||
|         socket.send({ | ||||
|             cmd: "DISPATCH", | ||||
|             evt: "READY", | ||||
|             data: {} | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     async onMessage(socket, {cmd, args}) { | ||||
|         switch (cmd) { | ||||
|             case "SET_ACTIVITY": | ||||
|                 if (!socket.application) { | ||||
|                     socket.application = await ( | ||||
|                         await fetch(`https://discord.com/api/v9/oauth2/applications/${socket.clientId}/rpc`) | ||||
|                     ).json(); | ||||
|                     socket.application.assets = await ( | ||||
|                         await fetch(`https://discord.com/api/v9/oauth2/applications/${socket.clientId}/assets`) | ||||
|                     ).json(); | ||||
|                     log("fetched app info for", socket.clientId, socket.application); | ||||
|                 } | ||||
| 
 | ||||
|                 const {activity, pid} = args; | ||||
|                 const {buttons, timestamps, instance} = activity; | ||||
| 
 | ||||
|                 const metadata = {}; | ||||
|                 const extra = {}; | ||||
|                 if (buttons) { | ||||
|                     metadata.button_urls = buttons.map((x) => x.url); | ||||
|                     extra.buttons = buttons.map((x) => x.label); | ||||
|                 } | ||||
| 
 | ||||
|                 if (timestamps) | ||||
|                     for (const x in timestamps) { | ||||
|                         if (Date.now().toString().length - timestamps[x].toString().length > 2) | ||||
|                             timestamps[x] = Math.floor(1e3 * timestamps[x]); | ||||
|                     } | ||||
| 
 | ||||
|                 if (activity.assets?.large_image) | ||||
|                     activity.assets.large_image = lookupAsset(activity.assets.large_image, socket.application.assets); | ||||
|                 if (activity.assets?.small_image) | ||||
|                     activity.assets.small_image = lookupAsset(activity.assets.small_image, socket.application.assets); | ||||
| 
 | ||||
|                 Bridge.send({ | ||||
|                     activity: { | ||||
|                         name: socket.application.name, | ||||
|                         application_id: socket.application.id, | ||||
|                         type: 0, | ||||
|                         metadata, | ||||
|                         flags: instance ? 1 << 0 : 0, | ||||
|                         ...activity, | ||||
|                         ...extra | ||||
|                     }, | ||||
|                     pid | ||||
|                 }); | ||||
| 
 | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| module.exports = {RPCServer}; | ||||
							
								
								
									
										255
									
								
								src/arrpc/src/transports/ipc.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								src/arrpc/src/transports/ipc.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,255 @@ | |||
| const rgb = (r, g, b, msg) => `\x1b[38;2;${r};${g};${b}m${msg}\x1b[0m`; | ||||
| const log = (...args) => console.log(`[${rgb(88, 101, 242, "arRPC")} > ${rgb(254, 231, 92, "ipc")}]`, ...args); | ||||
| const path = require("path"); | ||||
| const {platform, env} = require("process"); | ||||
| const {unlinkSync} = require("fs"); | ||||
| const {createServer, createConnection} = require("net"); | ||||
| 
 | ||||
| const SOCKET_PATH = | ||||
|     platform === "win32" | ||||
|         ? "\\\\?\\pipe\\discord-ipc" | ||||
|         : path.join(env.XDG_RUNTIME_DIR || env.TMPDIR || env.TMP || env.TEMP || "/tmp", "discord-ipc"); | ||||
| 
 | ||||
| const Types = { | ||||
|     HANDSHAKE: 0, | ||||
|     FRAME: 1, | ||||
|     CLOSE: 2, | ||||
|     PING: 3, | ||||
|     PONG: 4 | ||||
| }; | ||||
| 
 | ||||
| const CloseCodes = { | ||||
|     CLOSE_NORMAL: 1000, | ||||
|     CLOSE_UNSUPPORTED: 1003, | ||||
|     CLOSE_ABNORMAL: 1006 | ||||
| }; | ||||
| 
 | ||||
| const ErrorCodes = { | ||||
|     INVALID_CLIENTID: 4000, | ||||
|     INVALID_ORIGIN: 4001, | ||||
|     RATELIMITED: 4002, | ||||
|     TOKEN_REVOKED: 4003, | ||||
|     INVALID_VERSION: 4004, | ||||
|     INVALID_ENCODING: 4005 | ||||
| }; | ||||
| 
 | ||||
| let uniqueId = 0; | ||||
| 
 | ||||
| const encode = (type, data) => { | ||||
|     data = JSON.stringify(data); | ||||
|     const dataSize = Buffer.byteLength(data); | ||||
| 
 | ||||
|     const buf = Buffer.alloc(dataSize + 8); | ||||
|     buf.writeInt32LE(type, 0); // type
 | ||||
|     buf.writeInt32LE(dataSize, 4); // data size
 | ||||
|     buf.write(data, 8, dataSize); // data
 | ||||
| 
 | ||||
|     return buf; | ||||
| }; | ||||
| 
 | ||||
| const read = (socket) => { | ||||
|     let resp = socket.read(8); | ||||
|     if (!resp) return; | ||||
| 
 | ||||
|     resp = Buffer.from(resp); | ||||
|     const type = resp.readInt32LE(0); | ||||
|     const dataSize = resp.readInt32LE(4); | ||||
| 
 | ||||
|     if (type < 0 || type >= Object.keys(Types).length) throw new Error("invalid type"); | ||||
| 
 | ||||
|     let data = socket.read(dataSize); | ||||
|     if (!data) throw new Error("failed reading data"); | ||||
| 
 | ||||
|     data = JSON.parse(Buffer.from(data).toString()); | ||||
| 
 | ||||
|     switch (type) { | ||||
|         case Types.PING: | ||||
|             socket.emit("ping", data); | ||||
|             socket.write(encode(Types.PONG, data)); | ||||
|             break; | ||||
| 
 | ||||
|         case Types.PONG: | ||||
|             socket.emit("pong", data); | ||||
|             break; | ||||
| 
 | ||||
|         case Types.HANDSHAKE: | ||||
|             if (socket._handshook) throw new Error("already handshook"); | ||||
| 
 | ||||
|             socket._handshook = true; | ||||
|             socket.emit("handshake", data); | ||||
|             break; | ||||
| 
 | ||||
|         case Types.FRAME: | ||||
|             if (!socket._handshook) throw new Error("need to handshake first"); | ||||
| 
 | ||||
|             socket.emit("request", data); | ||||
|             break; | ||||
| 
 | ||||
|         case Types.CLOSE: | ||||
|             socket.end(); | ||||
|             socket.destroy(); | ||||
|             break; | ||||
|     } | ||||
| 
 | ||||
|     read(socket); | ||||
| }; | ||||
| 
 | ||||
| const socketIsAvailable = async (socket) => { | ||||
|     socket.pause(); | ||||
|     socket.on("readable", () => { | ||||
|         try { | ||||
|             read(socket); | ||||
|         } catch (e) { | ||||
|             log("error whilst reading", e); | ||||
| 
 | ||||
|             socket.end( | ||||
|                 encode(Types.CLOSE, { | ||||
|                     code: CloseCodes.CLOSE_UNSUPPORTED, | ||||
|                     message: e.message | ||||
|                 }) | ||||
|             ); | ||||
|             socket.destroy(); | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     const stop = () => { | ||||
|         try { | ||||
|             socket.end(); | ||||
|             socket.destroy(); | ||||
|         } catch {} | ||||
|     }; | ||||
| 
 | ||||
|     const possibleOutcomes = Promise.race([ | ||||
|         new Promise((res) => socket.on("error", res)), // errore
 | ||||
|         new Promise((res, rej) => socket.on("pong", () => rej("socket ponged"))), // ponged
 | ||||
|         new Promise((res, rej) => setTimeout(() => rej("timed out"), 1000)) // timed out
 | ||||
|     ]).then( | ||||
|         () => true, | ||||
|         (e) => e | ||||
|     ); | ||||
| 
 | ||||
|     socket.write(encode(Types.PING, ++uniqueId)); | ||||
| 
 | ||||
|     const outcome = await possibleOutcomes; | ||||
|     stop(); | ||||
|     log("checked if socket is available:", outcome === true, outcome === true ? "" : `- reason: ${outcome}`); | ||||
| 
 | ||||
|     return outcome === true; | ||||
| }; | ||||
| 
 | ||||
| const getAvailableSocket = async (tries = 0) => { | ||||
|     if (tries > 9) { | ||||
|         throw new Error("ran out of tries to find socket", tries); | ||||
|     } | ||||
| 
 | ||||
|     const path = SOCKET_PATH + "-" + tries; | ||||
|     const socket = createConnection(path); | ||||
| 
 | ||||
|     log("checking", path); | ||||
| 
 | ||||
|     if (await socketIsAvailable(socket)) { | ||||
|         if (platform !== "win32") | ||||
|             try { | ||||
|                 unlinkSync(path); | ||||
|             } catch {} | ||||
| 
 | ||||
|         return path; | ||||
|     } | ||||
| 
 | ||||
|     log(`not available, trying again (attempt ${tries + 1})`); | ||||
|     return getAvailableSocket(tries + 1); | ||||
| }; | ||||
| 
 | ||||
| class IPCServer { | ||||
|     constructor(messageHandler, connectionHandler) { | ||||
|         return new Promise(async (res) => { | ||||
|             this.messageHandler = messageHandler; | ||||
|             this.connectionHandler = connectionHandler; | ||||
| 
 | ||||
|             this.onConnection = this.onConnection.bind(this); | ||||
|             this.onMessage = this.onMessage.bind(this); | ||||
| 
 | ||||
|             const server = createServer(this.onConnection); | ||||
|             server.on("error", (e) => { | ||||
|                 log("server error", e); | ||||
|             }); | ||||
| 
 | ||||
|             const socketPath = await getAvailableSocket(); | ||||
|             server.listen(socketPath, () => { | ||||
|                 log("listening at", socketPath); | ||||
|                 this.server = server; | ||||
| 
 | ||||
|                 res(this); | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     onConnection(socket) { | ||||
|         log("new connection!"); | ||||
| 
 | ||||
|         socket.pause(); | ||||
|         socket.on("readable", () => { | ||||
|             try { | ||||
|                 read(socket); | ||||
|             } catch (e) { | ||||
|                 log("error whilst reading", e); | ||||
| 
 | ||||
|                 socket.end( | ||||
|                     encode(Types.CLOSE, { | ||||
|                         code: CloseCodes.CLOSE_UNSUPPORTED, | ||||
|                         message: e.message | ||||
|                     }) | ||||
|                 ); | ||||
|                 socket.destroy(); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         socket.once("handshake", (params) => { | ||||
|             log("handshake:", params); | ||||
| 
 | ||||
|             const ver = params.v ?? 1; | ||||
|             const clientId = params.client_id ?? ""; | ||||
| 
 | ||||
|             if (ver !== 1) { | ||||
|                 log("unsupported version requested", ver); | ||||
| 
 | ||||
|                 socket.close(ErrorCodes.INVALID_VERSION); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             if (clientId === "") { | ||||
|                 log("client id required"); | ||||
| 
 | ||||
|                 socket.close(ErrorCodes.INVALID_CLIENTID); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             socket.on("error", (e) => { | ||||
|                 log("socket error", e); | ||||
|             }); | ||||
| 
 | ||||
|             socket.on("close", (e) => { | ||||
|                 log("socket closed", e); | ||||
|             }); | ||||
| 
 | ||||
|             socket.on("request", this.onMessage.bind(this, socket)); | ||||
| 
 | ||||
|             socket._send = socket.send; | ||||
|             socket.send = (msg) => { | ||||
|                 log("sending", msg); | ||||
|                 socket.write(encode(Types.FRAME, msg)); | ||||
|             }; | ||||
| 
 | ||||
|             socket.clientId = clientId; | ||||
| 
 | ||||
|             this.connectionHandler(socket); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     onMessage(socket, msg) { | ||||
|         log("message", msg); | ||||
|         this.messageHandler(socket, msg); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| module.exports = {IPCServer}; | ||||
							
								
								
									
										124
									
								
								src/arrpc/src/transports/websocket.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/arrpc/src/transports/websocket.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,124 @@ | |||
| const rgb = (r, g, b, msg) => `\x1b[38;2;${r};${g};${b}m${msg}\x1b[0m`; | ||||
| const log = (...args) => console.log(`[${rgb(88, 101, 242, "arRPC")} > ${rgb(235, 69, 158, "websocket")}]`, ...args); | ||||
| const ws = require("ws"); | ||||
| const {createServer} = require("http"); | ||||
| const querystring = require("querystring"); | ||||
| 
 | ||||
| const portRange = [6463, 6472]; | ||||
| 
 | ||||
| class WSServer { | ||||
|     constructor(messageHandler, connectionHandler) { | ||||
|         return new Promise(async (res) => { | ||||
|             this.messageHandler = messageHandler; | ||||
|             this.connectionHandler = connectionHandler; | ||||
| 
 | ||||
|             this.onConnection = this.onConnection.bind(this); | ||||
|             this.onMessage = this.onMessage.bind(this); | ||||
| 
 | ||||
|             let port = portRange[0]; | ||||
| 
 | ||||
|             let http, wss; | ||||
|             while (port <= portRange[1]) { | ||||
|                 try { | ||||
|                     log("trying port", port); | ||||
| 
 | ||||
|                     http = createServer(); | ||||
|                     http.on("error", (e) => { | ||||
|                         log("http error", e); | ||||
| 
 | ||||
|                         if (e.code === "EADDRINUSE") { | ||||
|                             log(port, "in use!"); | ||||
|                         } | ||||
|                     }); | ||||
| 
 | ||||
|                     wss = new ws.WebSocketServer({server: http}); | ||||
|                     wss.on("error", (e) => { | ||||
|                         log("wss error", e); | ||||
|                     }); | ||||
| 
 | ||||
|                     wss.on("connection", this.onConnection); | ||||
| 
 | ||||
|                     http.listen(port, "127.0.0.1", () => { | ||||
|                         log("listening on", port); | ||||
| 
 | ||||
|                         this.http = http; | ||||
|                         this.wss = wss; | ||||
| 
 | ||||
|                         res(this); | ||||
|                     }); | ||||
|                 } catch (e) { | ||||
|                     log("failed to start", e); | ||||
|                 } | ||||
| 
 | ||||
|                 break; | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     onConnection(socket, req) { | ||||
|         const params = querystring.parse(req.url.split("?")[1]); | ||||
|         const ver = parseInt(params.v ?? 1); | ||||
|         const encoding = params.encoding ?? "json"; | ||||
|         const clientId = params.client_id ?? ""; | ||||
| 
 | ||||
|         const origin = req.headers.origin ?? ""; | ||||
| 
 | ||||
|         log(`new connection! origin:`, origin, JSON.parse(JSON.stringify(params))); | ||||
| 
 | ||||
|         if (origin !== "") { | ||||
|             log("origin is defined, denying", origin); | ||||
| 
 | ||||
|             socket.close(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (encoding !== "json") { | ||||
|             log("unsupported encoding requested", encoding); | ||||
| 
 | ||||
|             socket.close(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (ver !== 1) { | ||||
|             log("unsupported version requested", ver); | ||||
| 
 | ||||
|             socket.close(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (clientId === "") { | ||||
|             log("client id required"); | ||||
| 
 | ||||
|             socket.close(); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         socket.clientId = clientId; | ||||
|         socket.encoding = encoding; | ||||
| 
 | ||||
|         socket.on("error", (e) => { | ||||
|             log("socket error", e); | ||||
|         }); | ||||
| 
 | ||||
|         socket.on("close", (e, r) => { | ||||
|             log("socket closed", e); | ||||
|         }); | ||||
| 
 | ||||
|         socket.on("message", this.onMessage.bind(this, socket)); | ||||
| 
 | ||||
|         socket._send = socket.send; | ||||
|         socket.send = (msg) => { | ||||
|             log("sending", msg); | ||||
|             socket._send(JSON.stringify(msg)); | ||||
|         }; | ||||
| 
 | ||||
|         this.connectionHandler(socket); | ||||
|     } | ||||
| 
 | ||||
|     onMessage(socket, msg) { | ||||
|         log("message", JSON.parse(msg)); | ||||
|         this.messageHandler(socket, JSON.parse(msg)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| module.exports = {WSServer}; | ||||
|  | @ -5,7 +5,7 @@ import "./patch"; | |||
| import * as fs from "fs"; | ||||
| import * as path from "path"; | ||||
| import {injectHummusTitlebar, injectTitlebar} from "./titlebar"; | ||||
| import {sleep, addStyle} from "../utils"; | ||||
| import {sleep, addStyle, addScript} from "../utils"; | ||||
| import {injectMobileStuff} from "./mobile"; | ||||
| var version = ipcRenderer.sendSync("displayVersion"); | ||||
| var channel = ipcRenderer.sendSync("channel"); | ||||
|  | @ -42,6 +42,41 @@ if (window.location.href.indexOf("splash.html") > -1) { | |||
|         injectMobileStuff(); | ||||
|     } | ||||
|     sleep(5000).then(async () => { | ||||
|         addScript(` | ||||
|         const dispatch = (() => { | ||||
|             let Dispatcher; | ||||
|            | ||||
|             return function (event) { | ||||
|               Dispatcher ??= window.Vencord?.Webpack.Common.FluxDispatcher | ||||
|               if (!Dispatcher) { | ||||
|                 const cache = webpackChunkdiscord_app.push([[Symbol()], {}, w => w]).c; | ||||
|                 webpackChunkdiscord_app.pop() | ||||
|            | ||||
|                 outer: | ||||
|                 for (const id in cache) { | ||||
|                   const mod = cache[id].exports; | ||||
|                   for (const exp in mod) { | ||||
|                     if (mod[exp]?.isDispatching) { | ||||
|                       Dispatcher = mod[exp]; | ||||
|                       break outer; | ||||
|                     } | ||||
|                   } | ||||
|                 } | ||||
|               } | ||||
|               if (!Dispatcher)  | ||||
|                 return; // failed to find, your choice if and how u wanna handle this
 | ||||
|            | ||||
|               return Dispatcher.dispatch(event); | ||||
|             }; | ||||
|           })(); | ||||
|           const ws = new WebSocket('ws://localhost:1337'); // connect to arRPC bridge
 | ||||
|           ws.onmessage = x => { | ||||
|             msg = JSON.parse(x.data); | ||||
|             console.log(msg); | ||||
|            | ||||
|             dispatch({ type: "LOCAL_ACTIVITY_UPDATE", ...msg }); // set RPC status
 | ||||
|           }; | ||||
|         `);
 | ||||
|         const cssPath = path.join(__dirname, "../", "/content/css/discord.css"); | ||||
|         addStyle(fs.readFileSync(cssPath, "utf8")); | ||||
|         await updateLang(); | ||||
|  |  | |||
|  | @ -205,7 +205,9 @@ async function doAfterDefiningTheWindow() { | |||
|     }); | ||||
|     console.log(contentPath); | ||||
|     if ((await getConfig("inviteWebsocket")) == true) { | ||||
|         await startServer(); | ||||
|         //@ts-ignore
 | ||||
|         import("arrpc") | ||||
|         //await startServer();
 | ||||
|     } | ||||
|     if (firstRun) { | ||||
|         await setLang(Intl.DateTimeFormat().resolvedOptions().locale); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue