mirror of
https://github.com/smartfrigde/armcord.git
synced 2024-08-14 23:56:58 +00:00
Make invite socket use arrpc
This commit is contained in:
parent
d7a318d5c3
commit
e6df2b0ecf
4 changed files with 17 additions and 199 deletions
|
@ -11,13 +11,8 @@ async function run() {
|
|||
server.on("activity", (data) => mainWindow.webContents.send("rpc", data));
|
||||
server.on("invite", (code) => {
|
||||
console.log(code);
|
||||
const {createInviteWindow, inviteWindow} = require("../../../ts-out/window.js");
|
||||
const {exportPort} = require("./transports/websocket.js");
|
||||
createInviteWindow();
|
||||
const win = inviteWindow;
|
||||
//doesnt work
|
||||
win.loadURL("https://discord.com/invite/" + code);
|
||||
win.show();
|
||||
const {createInviteWindow} = require("../../../ts-out/window.js");
|
||||
createInviteWindow(code);
|
||||
});
|
||||
}
|
||||
run();
|
||||
|
|
186
src/socket.ts
186
src/socket.ts
|
@ -1,186 +0,0 @@
|
|||
import type {Server, WebSocket} from "ws";
|
||||
import {inviteWindow, createInviteWindow} from "./window";
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-2022 Dawid Papiewski "SpacingBat3"
|
||||
|
||||
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.
|
||||
*/
|
||||
async function wsLog(message: string, ...args: unknown[]) {
|
||||
console.log("[WebSocket] " + message, ...args);
|
||||
}
|
||||
|
||||
/** Generates an inclusive range (as `Array`) from `start` to `end`. */
|
||||
function range(start: number, end: number) {
|
||||
return Array.from({length: end - start + 1}, (_v, k) => start + k);
|
||||
}
|
||||
|
||||
interface InviteResponse {
|
||||
/** Response type/command. */
|
||||
cmd: "INVITE_BROWSER";
|
||||
/** Response arguments. */
|
||||
args: {
|
||||
/** An invitation code. */
|
||||
code: string;
|
||||
};
|
||||
/** Nonce indentifying the communication. */
|
||||
nonce: string;
|
||||
}
|
||||
|
||||
function isInviteResponse(data: unknown): data is InviteResponse {
|
||||
if (!(data instanceof Object)) return false;
|
||||
if ((data as Partial<InviteResponse>)?.cmd !== "INVITE_BROWSER") return false;
|
||||
if (typeof (data as Partial<InviteResponse>)?.args?.code !== "string") return false;
|
||||
if (typeof (data as Partial<InviteResponse>)?.nonce !== "string") return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
const messages = {
|
||||
/**
|
||||
* A fake, hard-coded Discord command to spoof the presence of
|
||||
* official Discord client (which makes browser to actually start a
|
||||
* communication with the ArmCord).
|
||||
*/
|
||||
handShake: {
|
||||
/** Message command. */
|
||||
cmd: "DISPATCH",
|
||||
/** Message data. */
|
||||
data: {
|
||||
/** Message scheme version. */
|
||||
v: 1,
|
||||
/** Client properties. */
|
||||
config: {
|
||||
/** Discord CDN host (hard-coded for `dicscord.com` instance). */
|
||||
cdn_host: "cdn.discordapp.com",
|
||||
/** API endpoint (hard-coded for `dicscord.com` instance). */
|
||||
api_endpoint: "//discord.com/api",
|
||||
/** Client type. Can be (probably) `production` or `canary`. */
|
||||
environment: "production"
|
||||
}
|
||||
},
|
||||
evt: "READY",
|
||||
nonce: null
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tries to reserve the server at given port.
|
||||
*
|
||||
* @returns `Promise`, which always resolves (either to `Server<WebSocket>` on
|
||||
* success or `null` on failure).
|
||||
*/
|
||||
async function getServer(port: number) {
|
||||
const {WebSocketServer} = await import("ws");
|
||||
return new Promise<Server<WebSocket> | null>((resolve) => {
|
||||
const wss = new WebSocketServer({host: "127.0.0.1", port});
|
||||
wss.once("listening", () => resolve(wss));
|
||||
wss.once("error", () => resolve(null));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to start a WebSocket server at given port range. If it suceed, it will
|
||||
* listen to the browser requests which are meant to be sent to official
|
||||
* Discord client.
|
||||
*
|
||||
* Currently it supports only the invitation link requests.
|
||||
*
|
||||
*/
|
||||
export default async function startServer() {
|
||||
function isJsonSyntaxCorrect(string: string) {
|
||||
try {
|
||||
JSON.parse(string);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/** Known Discord instances, including the official ones. */
|
||||
const knownInstancesList = [
|
||||
["Discord", new URL("https://discord.com/app")],
|
||||
["Discord Canary", new URL("https://canary.discord.com/app")],
|
||||
["Discord PTB", new URL("https://ptb.discord.com/app")],
|
||||
["Fosscord", new URL("https://dev.fosscord.com/app")]
|
||||
] as const;
|
||||
|
||||
let wss = null,
|
||||
wsPort = 6463;
|
||||
for (const port of range(6463, 6472)) {
|
||||
wss = await getServer(port);
|
||||
if (wss !== null) {
|
||||
void wsLog("ArmCord is listening at " + port.toString());
|
||||
wsPort = port;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (wss === null) return;
|
||||
let lock = false;
|
||||
wss.on("connection", (wss, request) => {
|
||||
const origin = request.headers.origin ?? "https://discord.com";
|
||||
let known = false;
|
||||
for (const instance of knownInstancesList) {
|
||||
if (instance[1].origin === origin) known = true;
|
||||
}
|
||||
if (!known) return;
|
||||
wss.send(JSON.stringify(messages.handShake));
|
||||
wss.once("message", (data, isBinary) => {
|
||||
if (lock) return;
|
||||
lock = true;
|
||||
let parsedData: unknown = data;
|
||||
if (!isBinary) parsedData = data.toString();
|
||||
if (isJsonSyntaxCorrect(parsedData as string)) parsedData = JSON.parse(parsedData as string);
|
||||
if (isInviteResponse(parsedData)) {
|
||||
// Replies to browser, so it finds the communication successful.
|
||||
wss.send(
|
||||
JSON.stringify({
|
||||
cmd: parsedData.cmd,
|
||||
data: {
|
||||
invite: null,
|
||||
code: parsedData.args.code
|
||||
},
|
||||
evt: null,
|
||||
nonce: parsedData.nonce
|
||||
})
|
||||
);
|
||||
createInviteWindow();
|
||||
const child = inviteWindow;
|
||||
if (child === undefined) return;
|
||||
void child.loadURL(origin + "/invite/" + parsedData.args.code);
|
||||
child.webContents.once("did-finish-load", () => {
|
||||
child.show();
|
||||
});
|
||||
child.webContents.once("will-navigate", () => {
|
||||
lock = false;
|
||||
child.close();
|
||||
});
|
||||
child.on("close", (e) => {
|
||||
lock = false;
|
||||
});
|
||||
// Blocks requests to ArmCord's WS, to prevent loops.
|
||||
child.webContents.session.webRequest.onBeforeRequest(
|
||||
{
|
||||
urls: ["ws://127.0.0.1:" + wsPort.toString() + "/*"]
|
||||
},
|
||||
(_details, callback) => callback({cancel: true})
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import * as fs from "fs";
|
||||
import {app, Menu, Tray, nativeImage} from "electron";
|
||||
import {mainWindow} from "./window";
|
||||
import {createInviteWindow, mainWindow} from "./window";
|
||||
import {getConfig, getConfigLocation, setWindowState, getDisplayVersion} from "./utils";
|
||||
import * as path from "path";
|
||||
import {createSettingsWindow} from "./settings/main";
|
||||
|
@ -113,8 +113,7 @@ app.whenReady().then(async () => {
|
|||
{
|
||||
label: "Support Discord Server",
|
||||
click: function () {
|
||||
mainWindow.show();
|
||||
mainWindow.loadURL("https://discord.gg/TnhxcqynZ2");
|
||||
createInviteWindow("TnhxcqynZ2");
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -17,7 +17,6 @@ import {
|
|||
import {registerIpc} from "./ipc";
|
||||
import {setMenu} from "./menu";
|
||||
import * as fs from "fs";
|
||||
import startServer from "./socket";
|
||||
import contextMenu from "electron-context-menu";
|
||||
import os from "os";
|
||||
import {tray} from "./tray";
|
||||
|
@ -292,7 +291,7 @@ export function createTransparentWindow() {
|
|||
});
|
||||
doAfterDefiningTheWindow();
|
||||
}
|
||||
export function createInviteWindow() {
|
||||
export function createInviteWindow(code: string) {
|
||||
inviteWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
|
@ -306,5 +305,16 @@ export function createInviteWindow() {
|
|||
spellcheck: true
|
||||
}
|
||||
});
|
||||
inviteWindow.hide();
|
||||
var formInviteURL = `https://discord.com/invite/${code}`;
|
||||
inviteWindow.webContents.session.webRequest.onBeforeRequest((details, callback) => {
|
||||
if (details.url.includes("ws://")) return callback({cancel: true});
|
||||
return callback({});
|
||||
});
|
||||
inviteWindow.loadURL(formInviteURL);
|
||||
inviteWindow.webContents.once("did-finish-load", () => {
|
||||
inviteWindow.show();
|
||||
inviteWindow.webContents.once("will-navigate", () => {
|
||||
inviteWindow.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue