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
				
			
		|  | @ -13,7 +13,7 @@ | |||
| 
 | ||||
| - **Various mods built in** | ||||
|   | ||||
|    Enjoy Cumcord, GooseMod, Flicker, and their many features, or have a more vanilla experience, it's your choice! | ||||
|    Enjoy Vencord, Shelter and their many features, or have a more vanilla experience, it's your choice! | ||||
| 
 | ||||
| 
 | ||||
| - **Made for Privacy™** | ||||
|  |  | |||
							
								
								
									
										146
									
								
								log.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								log.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,146 @@ | |||
| 
 | ||||
| > ArmCord@3.1.0 start | ||||
| > npm run build && electron ./ts-out/main.js | ||||
| 
 | ||||
| 
 | ||||
| > ArmCord@3.1.0 build | ||||
| > tsc && copyfiles -u 1 src/**/*.html src/**/**/*.css ts-out/ && copyfiles package.json ts-out/ && copyfiles assets/**/** ts-out/ | ||||
| 
 | ||||
| [Config manager] doneSetup: undefined | ||||
| [Config manager] performanceMode: none | ||||
| ArmCord has been run before. Skipping setup. | ||||
| No performance modes set | ||||
| [Config manager] windowStyle: default | ||||
| [Config manager] armcordCSP: true | ||||
| [Config manager] doneSetup: undefined | ||||
| [Config manager] customIcon: undefined | ||||
| Setting up CSP unstricter... | ||||
| [Config manager] trayIcon: default | ||||
| [Config manager] windowStyle: default | ||||
| [Config manager] windowStyle: default | ||||
| [Config manager] ignoreProtocolWarning: undefined | ||||
| [Config manager] clientName: undefined | ||||
| [Config manager] 0: undefined | ||||
| [Config manager] mods: vencord | ||||
| Downloading mod bundle | ||||
| [Config manager] mods: vencord | ||||
| [Config manager] mobileMode: false | ||||
| [Config manager] trayIcon: default | ||||
| [Mod loader] Loaded ArmCord Mod Loader made by Vendicated | ||||
| [Config manager] alternativePaste: false | ||||
| undefined | ||||
| [Config manager] inviteWebsocket: true | ||||
| [Config manager] skipSplash: undefined | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checking /run/user/1000/discord-ipc-0 | ||||
| Error: connect ECONNREFUSED /run/user/1000/discord-ipc-0 | ||||
|     at PipeConnectWrap.afterConnect [as oncomplete] (node:net:1187:16) { | ||||
|   errno: -111, | ||||
|   code: 'ECONNREFUSED', | ||||
|   syscall: 'connect', | ||||
|   address: '/run/user/1000/discord-ipc-0' | ||||
| } | ||||
| [Config manager] channel: stable | ||||
| [Config manager] mods: vencord | ||||
| [Config manager] automaticPatches: false | ||||
| [Config manager] channel: stable | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checked if socket is available: false - reason: timed out | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] not available, trying again (attempt 1) | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checking /run/user/1000/discord-ipc-1 | ||||
| Error: connect ENOENT /run/user/1000/discord-ipc-1 | ||||
|     at PipeConnectWrap.afterConnect [as oncomplete] (node:net:1187:16) { | ||||
|   errno: -2, | ||||
|   code: 'ENOENT', | ||||
|   syscall: 'connect', | ||||
|   address: '/run/user/1000/discord-ipc-1' | ||||
| } | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checked if socket is available: false - reason: timed out | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] not available, trying again (attempt 2) | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checking /run/user/1000/discord-ipc-2 | ||||
| Error: connect ENOENT /run/user/1000/discord-ipc-2 | ||||
|     at PipeConnectWrap.afterConnect [as oncomplete] (node:net:1187:16) { | ||||
|   errno: -2, | ||||
|   code: 'ENOENT', | ||||
|   syscall: 'connect', | ||||
|   address: '/run/user/1000/discord-ipc-2' | ||||
| } | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checked if socket is available: false - reason: timed out | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] not available, trying again (attempt 3) | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checking /run/user/1000/discord-ipc-3 | ||||
| Error: connect ENOENT /run/user/1000/discord-ipc-3 | ||||
|     at PipeConnectWrap.afterConnect [as oncomplete] (node:net:1187:16) { | ||||
|   errno: -2, | ||||
|   code: 'ENOENT', | ||||
|   syscall: 'connect', | ||||
|   address: '/run/user/1000/discord-ipc-3' | ||||
| } | ||||
| [Window state manager] width: 800 | ||||
| [Window state manager] height: 600 | ||||
| [Window state manager] isMaximized: false | ||||
| [Window state manager] Not maximized. | ||||
| [Config manager] channel: stable | ||||
| [Config manager] mods: vencord | ||||
| [Config manager] automaticPatches: false | ||||
| [Config manager] channel: stable | ||||
| [Config manager] mobileMode: false | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checked if socket is available: false - reason: timed out | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] not available, trying again (attempt 4) | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checking /run/user/1000/discord-ipc-4 | ||||
| Error: connect ENOENT /run/user/1000/discord-ipc-4 | ||||
|     at PipeConnectWrap.afterConnect [as oncomplete] (node:net:1187:16) { | ||||
|   errno: -2, | ||||
|   code: 'ENOENT', | ||||
|   syscall: 'connect', | ||||
|   address: '/run/user/1000/discord-ipc-4' | ||||
| } | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checked if socket is available: false - reason: timed out | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] not available, trying again (attempt 5) | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checking /run/user/1000/discord-ipc-5 | ||||
| Error: connect ENOENT /run/user/1000/discord-ipc-5 | ||||
|     at PipeConnectWrap.afterConnect [as oncomplete] (node:net:1187:16) { | ||||
|   errno: -2, | ||||
|   code: 'ENOENT', | ||||
|   syscall: 'connect', | ||||
|   address: '/run/user/1000/discord-ipc-5' | ||||
| } | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checked if socket is available: false - reason: timed out | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] not available, trying again (attempt 6) | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checking /run/user/1000/discord-ipc-6 | ||||
| Error: connect ENOENT /run/user/1000/discord-ipc-6 | ||||
|     at PipeConnectWrap.afterConnect [as oncomplete] (node:net:1187:16) { | ||||
|   errno: -2, | ||||
|   code: 'ENOENT', | ||||
|   syscall: 'connect', | ||||
|   address: '/run/user/1000/discord-ipc-6' | ||||
| } | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checked if socket is available: false - reason: timed out | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] not available, trying again (attempt 7) | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checking /run/user/1000/discord-ipc-7 | ||||
| Error: connect ENOENT /run/user/1000/discord-ipc-7 | ||||
|     at PipeConnectWrap.afterConnect [as oncomplete] (node:net:1187:16) { | ||||
|   errno: -2, | ||||
|   code: 'ENOENT', | ||||
|   syscall: 'connect', | ||||
|   address: '/run/user/1000/discord-ipc-7' | ||||
| } | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checked if socket is available: false - reason: timed out | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] not available, trying again (attempt 8) | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checking /run/user/1000/discord-ipc-8 | ||||
| Error: connect ENOENT /run/user/1000/discord-ipc-8 | ||||
|     at PipeConnectWrap.afterConnect [as oncomplete] (node:net:1187:16) { | ||||
|   errno: -2, | ||||
|   code: 'ENOENT', | ||||
|   syscall: 'connect', | ||||
|   address: '/run/user/1000/discord-ipc-8' | ||||
| } | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checked if socket is available: false - reason: timed out | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] not available, trying again (attempt 9) | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checking /run/user/1000/discord-ipc-9 | ||||
| Error: connect ENOENT /run/user/1000/discord-ipc-9 | ||||
|     at PipeConnectWrap.afterConnect [as oncomplete] (node:net:1187:16) { | ||||
|   errno: -2, | ||||
|   code: 'ENOENT', | ||||
|   syscall: 'connect', | ||||
|   address: '/run/user/1000/discord-ipc-9' | ||||
| } | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] checked if socket is available: false - reason: timed out | ||||
| [[38;2;88;101;242marRPC[0m > [38;2;254;231;92mipc[0m] not available, trying again (attempt 10) | ||||
|  | @ -43,6 +43,7 @@ | |||
|     "electron-context-menu": "github:ArmCord/electron-context-menu", | ||||
|     "extract-zip": "^2.0.1", | ||||
|     "node-fetch": "v2", | ||||
|     "arrpc": "file:./src/arrpc", | ||||
|     "os-locale": "^6.0.2", | ||||
|     "v8-compile-cache": "^2.3.0", | ||||
|     "ws": "^8.8.0" | ||||
|  |  | |||
							
								
								
									
										26
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										26
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							|  | @ -5,6 +5,7 @@ specifiers: | |||
|   '@types/node': ^17.0.42 | ||||
|   '@types/node-fetch': ^2.6.2 | ||||
|   '@types/ws': ^8.5.3 | ||||
|   arrpc: file:./src/arrpc | ||||
|   chalk-cli: ^5.0.0 | ||||
|   copyfiles: ^2.4.1 | ||||
|   electron: ^20.1.0 | ||||
|  | @ -21,6 +22,7 @@ specifiers: | |||
| 
 | ||||
| dependencies: | ||||
|   '@pyke/vibe': github.com/pykeio/vibe/11984868ce9e007859ed91ff159c7f7f0a34e7ae_electron@20.3.1 | ||||
|   arrpc: file:src/arrpc | ||||
|   electron-context-menu: github.com/ArmCord/electron-context-menu/280c81398c02a063f46e3285a9708d8db1a7ce32 | ||||
|   extract-zip: 2.0.1 | ||||
|   node-fetch: 2.6.7 | ||||
|  | @ -2188,6 +2190,19 @@ packages: | |||
|   /wrappy/1.0.2: | ||||
|     resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} | ||||
| 
 | ||||
|   /ws/8.11.0: | ||||
|     resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==} | ||||
|     engines: {node: '>=10.0.0'} | ||||
|     peerDependencies: | ||||
|       bufferutil: ^4.0.1 | ||||
|       utf-8-validate: ^5.0.2 | ||||
|     peerDependenciesMeta: | ||||
|       bufferutil: | ||||
|         optional: true | ||||
|       utf-8-validate: | ||||
|         optional: true | ||||
|     dev: false | ||||
| 
 | ||||
|   /ws/8.9.0: | ||||
|     resolution: {integrity: sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==} | ||||
|     engines: {node: '>=10.0.0'} | ||||
|  | @ -2267,6 +2282,17 @@ packages: | |||
|     engines: {node: '>=10'} | ||||
|     dev: true | ||||
| 
 | ||||
|   file:src/arrpc: | ||||
|     resolution: {directory: src/arrpc, type: directory} | ||||
|     name: arrpc | ||||
|     version: 0.1.0 | ||||
|     dependencies: | ||||
|       ws: 8.11.0 | ||||
|     transitivePeerDependencies: | ||||
|       - bufferutil | ||||
|       - utf-8-validate | ||||
|     dev: false | ||||
| 
 | ||||
|   github.com/ArmCord/electron-context-menu/280c81398c02a063f46e3285a9708d8db1a7ce32: | ||||
|     resolution: {tarball: https://codeload.github.com/ArmCord/electron-context-menu/tar.gz/280c81398c02a063f46e3285a9708d8db1a7ce32} | ||||
|     name: electron-context-menu | ||||
|  |  | |||
							
								
								
									
										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