Add experimental arrpc

This commit is contained in:
smartfridge 2022-11-19 23:15:05 +01:00
parent 0bd294480d
commit a0e0f6a516
16 changed files with 813 additions and 3 deletions

View file

@ -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
View 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
[arRPC > ipc] 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
[arRPC > ipc] checked if socket is available: false - reason: timed out
[arRPC > ipc] not available, trying again (attempt 1)
[arRPC > ipc] 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'
}
[arRPC > ipc] checked if socket is available: false - reason: timed out
[arRPC > ipc] not available, trying again (attempt 2)
[arRPC > ipc] 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'
}
[arRPC > ipc] checked if socket is available: false - reason: timed out
[arRPC > ipc] not available, trying again (attempt 3)
[arRPC > ipc] 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
[arRPC > ipc] checked if socket is available: false - reason: timed out
[arRPC > ipc] not available, trying again (attempt 4)
[arRPC > ipc] 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'
}
[arRPC > ipc] checked if socket is available: false - reason: timed out
[arRPC > ipc] not available, trying again (attempt 5)
[arRPC > ipc] 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'
}
[arRPC > ipc] checked if socket is available: false - reason: timed out
[arRPC > ipc] not available, trying again (attempt 6)
[arRPC > ipc] 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'
}
[arRPC > ipc] checked if socket is available: false - reason: timed out
[arRPC > ipc] not available, trying again (attempt 7)
[arRPC > ipc] 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'
}
[arRPC > ipc] checked if socket is available: false - reason: timed out
[arRPC > ipc] not available, trying again (attempt 8)
[arRPC > ipc] 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'
}
[arRPC > ipc] checked if socket is available: false - reason: timed out
[arRPC > ipc] not available, trying again (attempt 9)
[arRPC > ipc] 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'
}
[arRPC > ipc] checked if socket is available: false - reason: timed out
[arRPC > ipc] not available, trying again (attempt 10)

View file

@ -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"

View file

@ -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
View file

@ -0,0 +1,2 @@
node_modules
package-lock.json

21
src/arrpc/LICENSE Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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};

View 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};

View 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};

View file

@ -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();

View file

@ -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);