Add experimental arrpc
This commit is contained in:
parent
0bd294480d
commit
a0e0f6a516
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
- **Various mods built in**
|
- **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™**
|
- **Made for Privacy™**
|
||||||
|
|
|
@ -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",
|
"electron-context-menu": "github:ArmCord/electron-context-menu",
|
||||||
"extract-zip": "^2.0.1",
|
"extract-zip": "^2.0.1",
|
||||||
"node-fetch": "v2",
|
"node-fetch": "v2",
|
||||||
|
"arrpc": "file:./src/arrpc",
|
||||||
"os-locale": "^6.0.2",
|
"os-locale": "^6.0.2",
|
||||||
"v8-compile-cache": "^2.3.0",
|
"v8-compile-cache": "^2.3.0",
|
||||||
"ws": "^8.8.0"
|
"ws": "^8.8.0"
|
||||||
|
|
|
@ -5,6 +5,7 @@ specifiers:
|
||||||
'@types/node': ^17.0.42
|
'@types/node': ^17.0.42
|
||||||
'@types/node-fetch': ^2.6.2
|
'@types/node-fetch': ^2.6.2
|
||||||
'@types/ws': ^8.5.3
|
'@types/ws': ^8.5.3
|
||||||
|
arrpc: file:./src/arrpc
|
||||||
chalk-cli: ^5.0.0
|
chalk-cli: ^5.0.0
|
||||||
copyfiles: ^2.4.1
|
copyfiles: ^2.4.1
|
||||||
electron: ^20.1.0
|
electron: ^20.1.0
|
||||||
|
@ -21,6 +22,7 @@ specifiers:
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@pyke/vibe': github.com/pykeio/vibe/11984868ce9e007859ed91ff159c7f7f0a34e7ae_electron@20.3.1
|
'@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
|
electron-context-menu: github.com/ArmCord/electron-context-menu/280c81398c02a063f46e3285a9708d8db1a7ce32
|
||||||
extract-zip: 2.0.1
|
extract-zip: 2.0.1
|
||||||
node-fetch: 2.6.7
|
node-fetch: 2.6.7
|
||||||
|
@ -2188,6 +2190,19 @@ packages:
|
||||||
/wrappy/1.0.2:
|
/wrappy/1.0.2:
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
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:
|
/ws/8.9.0:
|
||||||
resolution: {integrity: sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==}
|
resolution: {integrity: sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
|
@ -2267,6 +2282,17 @@ packages:
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: true
|
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:
|
github.com/ArmCord/electron-context-menu/280c81398c02a063f46e3285a9708d8db1a7ce32:
|
||||||
resolution: {tarball: https://codeload.github.com/ArmCord/electron-context-menu/tar.gz/280c81398c02a063f46e3285a9708d8db1a7ce32}
|
resolution: {tarball: https://codeload.github.com/ArmCord/electron-context-menu/tar.gz/280c81398c02a063f46e3285a9708d8db1a7ce32}
|
||||||
name: electron-context-menu
|
name: electron-context-menu
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
node_modules
|
||||||
|
package-lock.json
|
|
@ -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.
|
|
@ -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
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
};
|
|
@ -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};
|
|
@ -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();
|
|
@ -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};
|
|
@ -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};
|
|
@ -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 fs from "fs";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import {injectHummusTitlebar, injectTitlebar} from "./titlebar";
|
import {injectHummusTitlebar, injectTitlebar} from "./titlebar";
|
||||||
import {sleep, addStyle} from "../utils";
|
import {sleep, addStyle, addScript} from "../utils";
|
||||||
import {injectMobileStuff} from "./mobile";
|
import {injectMobileStuff} from "./mobile";
|
||||||
var version = ipcRenderer.sendSync("displayVersion");
|
var version = ipcRenderer.sendSync("displayVersion");
|
||||||
var channel = ipcRenderer.sendSync("channel");
|
var channel = ipcRenderer.sendSync("channel");
|
||||||
|
@ -42,6 +42,41 @@ if (window.location.href.indexOf("splash.html") > -1) {
|
||||||
injectMobileStuff();
|
injectMobileStuff();
|
||||||
}
|
}
|
||||||
sleep(5000).then(async () => {
|
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");
|
const cssPath = path.join(__dirname, "../", "/content/css/discord.css");
|
||||||
addStyle(fs.readFileSync(cssPath, "utf8"));
|
addStyle(fs.readFileSync(cssPath, "utf8"));
|
||||||
await updateLang();
|
await updateLang();
|
||||||
|
|
|
@ -205,7 +205,9 @@ async function doAfterDefiningTheWindow() {
|
||||||
});
|
});
|
||||||
console.log(contentPath);
|
console.log(contentPath);
|
||||||
if ((await getConfig("inviteWebsocket")) == true) {
|
if ((await getConfig("inviteWebsocket")) == true) {
|
||||||
await startServer();
|
//@ts-ignore
|
||||||
|
import("arrpc")
|
||||||
|
//await startServer();
|
||||||
}
|
}
|
||||||
if (firstRun) {
|
if (firstRun) {
|
||||||
await setLang(Intl.DateTimeFormat().resolvedOptions().locale);
|
await setLang(Intl.DateTimeFormat().resolvedOptions().locale);
|
||||||
|
|
Loading…
Reference in New Issue