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"
|
||||
|
|
|
@ -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…
Reference in a new issue