feat: rewrite to esm

This commit is contained in:
smartfrigde 2024-05-15 20:14:18 +02:00
parent 419cb8eb4a
commit 0487507bc4
48 changed files with 121 additions and 51054 deletions

View file

@ -7,7 +7,7 @@
"node": ">=18.0.0"
},
"scripts": {
"build": "tsc && copyfiles -u 1 src/**/*.html src/**/**/*.css src/**/**/*.js ts-out/ && copyfiles package.json ts-out/ && copyfiles assets/**/** ts-out/",
"build": "tsc && copyfiles -u 1 src/**/*.html src/**/**/*.css src/**/**/*.js ts-out/ && copyfiles package.json ts-out/ && copyfiles assets/**/** ts-out/ && npm run fixEsmImportPath",
"watch": "tsc -w",
"start": "npm run build && electron ./ts-out/main.js",
"startThemeManager": "npm run build && electron ./ts-out/main.js themes",
@ -16,6 +16,7 @@
"package": "npm run build && electron-builder",
"packageQuick": "npm run build && electron-builder --dir",
"format": "prettier --write src *.json",
"fixEsmImportPath": "fix-esm-import-path ts-out/main.js",
"lint": "eslint src --ext .js,.jsx,.ts,.tsx --ignore-path .gitignore",
"CIbuild": "npm run build && electron-builder --linux zip && electron-builder --windows zip && electron-builder --macos zip",
"prepare": "git config --local core.hooksPath .hooks/"
@ -26,27 +27,29 @@
},
"author": "smartfrigde",
"license": "OSL-3.0",
"type": "module",
"bugs": {
"url": "https://github.com/armcord/armcord/issues"
},
"homepage": "https://github.com/armcord/armcord#readme",
"devDependencies": {
"@types/node": "^18.11.9",
"@types/node": "^20.11.1",
"@types/ws": "^8.5.3",
"@typescript-eslint/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.59.2",
"copyfiles": "^2.4.1",
"electron": "30.0.4",
"electron": "30.0.5",
"electron-builder": "^24.13.3",
"eslint": "^8.40.0",
"eslint-config-dmitmel": "github:dmitmel/eslint-config-dmitmel",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1",
"fix-esm-import-path": "github:smartfrigde/fix-esm-import-path",
"prettier": "^2.7.1",
"typescript": "^4.9.3"
},
"dependencies": {
"arrpc": "file:src/arrpc",
"arrpc": "github:OpenAsar/arrpc",
"cross-fetch": "^3.1.5",
"electron-context-menu": "github:ArmCord/electron-context-menu",
"extract-zip": "^2.0.1",

View file

@ -6,8 +6,8 @@ settings:
dependencies:
arrpc:
specifier: file:src/arrpc
version: file:src/arrpc
specifier: github:OpenAsar/arrpc
version: github.com/OpenAsar/arrpc/c62ec6a04c8d870530aa6944257fe745f6c59a24
cross-fetch:
specifier: ^3.1.5
version: 3.1.5
@ -26,8 +26,8 @@ dependencies:
devDependencies:
'@types/node':
specifier: ^18.11.9
version: 18.11.9
specifier: ^20.11.1
version: 20.11.28
'@types/ws':
specifier: ^8.5.3
version: 8.5.3
@ -41,8 +41,8 @@ devDependencies:
specifier: ^2.4.1
version: 2.4.1
electron:
specifier: 30.0.4
version: 30.0.4
specifier: 30.0.5
version: 30.0.5
electron-builder:
specifier: ^24.13.3
version: 24.13.3(electron-builder-squirrel-windows@24.13.3)
@ -58,6 +58,9 @@ devDependencies:
eslint-plugin-prettier:
specifier: ^4.2.1
version: 4.2.1(eslint@8.40.0)(prettier@2.7.1)
fix-esm-import-path:
specifier: github:smartfrigde/fix-esm-import-path
version: github.com/smartfrigde/fix-esm-import-path/71f374903884f2da21aad60b8c6a34c144523bbf
prettier:
specifier: ^2.7.1
version: 2.7.1
@ -267,7 +270,7 @@ packages:
dependencies:
'@types/http-cache-semantics': 4.0.1
'@types/keyv': 3.1.4
'@types/node': 18.15.11
'@types/node': 20.11.28
'@types/responselike': 1.0.0
dev: true
@ -280,7 +283,7 @@ packages:
/@types/fs-extra@9.0.13:
resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==}
dependencies:
'@types/node': 18.15.11
'@types/node': 20.11.28
dev: true
/@types/http-cache-semantics@4.0.1:
@ -294,31 +297,23 @@ packages:
/@types/keyv@3.1.4:
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
dependencies:
'@types/node': 18.15.11
'@types/node': 20.11.28
dev: true
/@types/ms@0.7.31:
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
dev: true
/@types/node@18.11.9:
resolution: {integrity: sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==}
dev: true
/@types/node@18.15.11:
resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==}
/@types/node@20.11.28:
resolution: {integrity: sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==}
dependencies:
undici-types: 5.26.5
dev: true
/@types/plist@3.0.2:
resolution: {integrity: sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==}
requiresBuild: true
dependencies:
'@types/node': 18.15.11
'@types/node': 20.11.28
xmlbuilder: 15.1.1
dev: true
optional: true
@ -326,7 +321,7 @@ packages:
/@types/responselike@1.0.0:
resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
dependencies:
'@types/node': 18.15.11
'@types/node': 20.11.28
dev: true
/@types/semver@7.3.13:
@ -342,14 +337,14 @@ packages:
/@types/ws@8.5.3:
resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==}
dependencies:
'@types/node': 18.11.9
'@types/node': 20.11.28
dev: true
/@types/yauzl@2.10.0:
resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
requiresBuild: true
dependencies:
'@types/node': 18.15.11
'@types/node': 20.11.28
optional: true
/@typescript-eslint/eslint-plugin@5.59.2(@typescript-eslint/parser@5.59.2)(eslint@8.40.0)(typescript@4.9.3):
@ -1128,8 +1123,8 @@ packages:
- supports-color
dev: true
/electron@30.0.4:
resolution: {integrity: sha512-z8tGQeiVM6CHj0t8PKWVhAB4FZTRNN0vSrd1jA4edX1aOjBztddk4d0Gyw91pQb4arIVf2RGPu0hYbHG2+vDcA==}
/electron@30.0.5:
resolution: {integrity: sha512-+a7PjcAq2HrfF1l+Ez8n0W9YeZIam7E9ERHEGs+L2dqKu7qxk8GNSEFoBEPCpLI00p/fc0d76L9IcLCQJdNFqA==}
engines: {node: '>= 12.20.55'}
hasBin: true
requiresBuild: true
@ -2644,7 +2639,6 @@ packages:
/undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
dev: true
/universalify@0.1.2:
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
@ -2824,16 +2818,6 @@ packages:
readable-stream: 3.6.2
dev: true
file:src/arrpc:
resolution: {directory: src/arrpc, type: directory}
name: arrpc
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
@ -2844,6 +2828,18 @@ packages:
electron-is-dev: 2.0.0
dev: false
github.com/OpenAsar/arrpc/c62ec6a04c8d870530aa6944257fe745f6c59a24:
resolution: {tarball: https://codeload.github.com/OpenAsar/arrpc/tar.gz/c62ec6a04c8d870530aa6944257fe745f6c59a24}
name: arrpc
version: 3.4.0
hasBin: true
dependencies:
ws: 8.11.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
dev: false
github.com/dmitmel/eslint-config-dmitmel/d97129ec35235415c6ae6a42299f55cdbb5d75fd(eslint@8.40.0):
resolution: {tarball: https://codeload.github.com/dmitmel/eslint-config-dmitmel/tar.gz/d97129ec35235415c6ae6a42299f55cdbb5d75fd}
id: github.com/dmitmel/eslint-config-dmitmel/d97129ec35235415c6ae6a42299f55cdbb5d75fd
@ -2855,3 +2851,14 @@ packages:
dependencies:
eslint: 8.40.0
dev: true
github.com/smartfrigde/fix-esm-import-path/71f374903884f2da21aad60b8c6a34c144523bbf:
resolution: {tarball: https://codeload.github.com/smartfrigde/fix-esm-import-path/tar.gz/71f374903884f2da21aad60b8c6a34c144523bbf}
name: fix-esm-import-path
version: 1.5.0
hasBin: true
dependencies:
debug: 4.3.4
transitivePeerDependencies:
- supports-color
dev: true

View file

@ -1,21 +0,0 @@
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.

View file

@ -1,82 +0,0 @@
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/19228318/202900211-95e8474b-edbb-4048-ba0b-a581a6d57fc4.png" width=300>
<img alt="arRPC" src="https://user-images.githubusercontent.com/19228318/203024061-064fc015-9096-40c3-9786-ad23d90414a6.png" width=300>
</picture> <br>
<a href="https://choosealicense.com/licenses/mit/l"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg"></a>
<a href="https://github.com/sponsors/CanadaHonk"><img alt="GitHub Sponsors" src="https://img.shields.io/github/sponsors/CanadaHonk?label=Sponsors&logo=github"></a>
<h3>An open implementation of Discord's local RPC servers</h3>
<h4>Allowing RPC where it was otherwise impossible, like Discord Web and custom clients</h4>
</div>
<br>
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. **arRPC is experimental and a work in progress, so expect bugs, etc.**
<br>
Rich Presence (RPC) is the name for how some apps can talk to Discord desktop on your PC via localhost servers to display detailed info about the app's state. This usually works via parts of Discord desktop natively doing things + parts of Discord web interpreting that and setting it as your status. arRPC is an open source implementation of the local RPC servers on your PC, allowing apps to talk to it thinking it was just normal Discord. It can then send that info to apps which usually don't get RPC, like Discord Web, ArmCord, etc. which can then set that as your status. This would otherwise not be possible, as web apps/browsers/etc can't just use Discord's already existing code and version.
- App with Discord RPC
- ~~Discord Desktop's native server~~ arRPC
- ~~Discord Web's setting~~ mod/plugin
<br>
## Usage
### Server (**REQUIRED**)
1. Have latest (>=18) Node installed
2. Clone GitHub repo
3. `npm install`
4. Run server with `node src`
### Web
#### No Mods
1. Get [the arRPC server running](#server-required)
2. With Discord open, run the content of [`examples/bridge_mod.js`](examples/bridge_mod.js) in Console (Ctrl+Shift+I).
#### Vencord
1. Get [the arRPC server running](#server-required)
2. Just enable the `WebRichPresence (arRPC)` Vencord plugin!
### Custom Clients
#### ArmCord
ArmCord has arRPC specially integrated, just enable the option in it's settings (server not required)!
#### Webcord
1. Get [the arRPC server running](#server-required)
2. Disable the `Use built-in Content Security Policy` option in Advanced settings: ![image](https://user-images.githubusercontent.com/19228318/202926723-93b772fc-f37d-47d4-81fd-b11c5d4051e8.png)
3. With Webcord open, run the content of [`examples/bridge_mod.js`](examples/bridge_mod.js) in the DevTools Console (Ctrl+Shift+I).
---
Then just use apps with Discord RPC like normal and they _should_ work!
<br>
## Supported
### Transports
- [x] WebSocket Server
- [x] JSON
- [ ] Erlpack
- [ ] HTTP Server
- [x] IPC
- [x] Process Scanning
### Commands
- [x] DISPATCH
- [x] SET_ACTIVITY
- [x] INVITE_BROWSER
- [x] GUILD_TEMPLATE_BROWSER
- [x] DEEP_LINK

View file

@ -1,31 +0,0 @@
# arRPC Changelog
## v3.0.0 [26-11-2022]
- **Added Process Scanning.** Now scans for detectable/verified games and tells Discord the app, allowing process detection whilst maintaining privacy (Discord does not see any/all processes, just the name and app ID).
- **Fixed RPC not fully working with more apps/libraries.** Now responds with a mock/fake arRPC user and the proper config, replies with confirmation, and supports blank activites fully.
- **Fixed a few minor Bridge bugs.** Fixed catchup not working with several apps.
## v2.2.1 [24-11-2022]
- IPC: Fix version given as string not being accepted
- IPC: Fix socket closing
## v2.2.0 [20-11-2022]
- Server: Move all looking up/fetching to client
## v2.1.0 [20-11-2022]
- Server: Stop activites when app disconnects
- Server: Added support for several apps shown at once (added `socketId`)
- Bridge: Catchup newly connected clients with last message by socket id
- Transports: Rewrote internal API to use handlers object
- API: Added parsing for GUILD_TEMPLATE_BROWSER
- API: Added parsing for DEEP_LINK
## v2.0.0 [20-11-2022]
- feat (breaking): moved asset lookup to client
- feat: add examples
- feat: add changelog

View file

@ -1,22 +0,0 @@
{
"name": "arrpc",
"version": "3.0.0",
"description": "Open Discord RPC server for atypical setups",
"main": "src/index.js",
"scripts": {
"start": "node src"
},
"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"
}
}

View file

@ -1,17 +0,0 @@
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")}]`, ...args);
log("arRPC v3.4.0 ArmCord");
const RPCServer = require("./server.js");
const {mainWindow} = require("../../../ts-out/window.js");
async function run() {
const server = await new RPCServer();
server.on("activity", (data) => mainWindow.webContents.send("rpc", data));
server.on("invite", (code) => {
console.log(code);
const {createInviteWindow} = require("../../../ts-out/window.js");
createInviteWindow(code);
});
}
run();

File diff suppressed because it is too large Load diff

View file

@ -1,118 +0,0 @@
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(237, 66, 69, "process")}]`, ...args);
const DetectableDB = require("./detectable.json");
const Natives = require("./native/index.js");
const Native = Natives[process.platform];
const timestamps = {},
names = {},
pids = {};
class ProcessServer {
constructor(handlers) {
if (!Native) return; // log('unsupported platform:', process.platform);
this.handlers = handlers;
this.scan = this.scan.bind(this);
this.scan();
setInterval(this.scan, 5000);
log("started");
}
async scan() {
// const startTime = performance.now();
const processes = await Native.getProcesses();
const ids = [];
// log(`got processed in ${(performance.now() - startTime).toFixed(2)}ms`);
for (const [pid, _path, args] of processes) {
const path = _path.toLowerCase().replaceAll("\\", "/");
const toCompare = [];
const splitPath = path.split("/");
for (let i = 1; i < splitPath.length; i++) {
toCompare.push(splitPath.slice(-i).join("/"));
}
for (const p of toCompare.slice()) {
// add more possible tweaked paths for less false negatives
toCompare.push(p.replace("64", "")); // remove 64bit identifiers-ish
toCompare.push(p.replace(".x64", ""));
toCompare.push(p.replace("x64", ""));
}
for (const {executables, id, name} of DetectableDB) {
if (
executables?.some((x) => {
if (x.is_launcher) return false;
if (
x.name[0] === ">"
? x.name.substring(1) !== toCompare[0]
: !toCompare.some((y) => x.name === y)
)
return false;
if (args && x.arguments) return args.join(" ").indexOf(x.arguments) > -1;
return true;
})
) {
names[id] = name;
pids[id] = pid;
ids.push(id);
if (!timestamps[id]) {
log("detected game!", name);
timestamps[id] = Date.now();
}
// Resending this on evry scan is intentional, so that in the case that arRPC scans processes before Discord, existing activities will be sent
this.handlers.message(
{
socketId: id
},
{
cmd: "SET_ACTIVITY",
args: {
activity: {
application_id: id,
name,
timestamps: {
start: timestamps[id]
}
},
pid
}
}
);
}
}
}
for (const id in timestamps) {
if (!ids.includes(id)) {
log("lost game!", names[id]);
delete timestamps[id];
this.handlers.message(
{
socketId: id
},
{
cmd: "SET_ACTIVITY",
args: {
activity: null,
pid: pids[id]
}
}
);
}
}
// log(`finished scan in ${(performance.now() - startTime).toFixed(2)}ms`);
// process.stdout.write(`\r${' '.repeat(100)}\r[${rgb(88, 101, 242, 'arRPC')} > ${rgb(237, 66, 69, 'process')}] scanned (took ${(performance.now() - startTime).toFixed(2)}ms)`);
}
}
module.exports = ProcessServer;

View file

@ -1,3 +0,0 @@
const win32 = require("./win32");
const linux = require("./linux");
module.exports = {win32, linux};

View file

@ -1,18 +0,0 @@
const {readFile, readdir} = require("fs/promises");
const getProcesses = async () =>
(
await Promise.all(
(
await readdir("/proc")
).map(
(pid) =>
+pid > 0 &&
readFile(`/proc/${pid}/cmdline`, "utf8").then(
(path) => [+pid, path.split("\0")[0], path.split("\0").slice(1)],
() => 0
)
)
)
).filter((x) => x);
module.exports = {getProcesses};

View file

@ -1,20 +0,0 @@
const {exec} = require("child_process");
const getProcesses = () =>
new Promise((res) =>
exec(`wmic process get ProcessID,ExecutablePath,Name /format:csv`, (e, out) => {
res(
out
.toString()
.split("\r\n")
.slice(2)
.map((x) => {
// [ProcessId, Name, ExecutablePath]
const parsed = x.trim().split(",").slice(1).reverse();
return [parseInt(parsed[0]) || parsed[0], parsed[2] || parsed[1]];
})
.filter((x) => x[1])
);
})
);
module.exports = {getProcesses};

View file

@ -1,173 +0,0 @@
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 {EventEmitter} = require("events");
const IPCServer = require("./transports/ipc.js");
const WSServer = require("./transports/websocket.js");
const ProcessServer = require("./process/index.js");
let socketId = 0;
class RPCServer extends EventEmitter {
constructor() {
super();
return (async () => {
this.onConnection = this.onConnection.bind(this);
this.onMessage = this.onMessage.bind(this);
this.onClose = this.onClose.bind(this);
const handlers = {
connection: this.onConnection,
message: this.onMessage,
close: this.onClose
};
this.ipc = await new IPCServer(handlers);
this.ws = await new WSServer(handlers);
if (!process.argv.includes("--no-process-scanning") && !process.env.ARRPC_NO_PROCESS_SCANNING)
this.process = await new ProcessServer(handlers);
return this;
})();
}
onConnection(socket) {
socket.send({
cmd: "DISPATCH",
data: {
v: 1,
config: {
cdn_host: "cdn.discordapp.com",
api_endpoint: "//discord.com/api",
environment: "production"
},
user: {
// mock user data using arRPC app/bot
id: "1045800378228281345",
username: "arrpc",
discriminator: "0",
global_name: "arRPC",
avatar: "cfefa4d9839fb4bdf030f91c2a13e95c",
avatar_decoration_data: null,
bot: false,
flags: 0,
premium_type: 0
}
},
evt: "READY",
nonce: null
});
socket.socketId = socketId++;
this.emit("connection", socket);
}
onClose(socket) {
this.emit("activity", {
activity: null,
pid: socket.lastPid,
socketId: socket.socketId.toString()
});
this.emit("close", socket);
}
async onMessage(socket, {cmd, args, nonce}) {
this.emit("message", {socket, cmd, args, nonce});
switch (cmd) {
case "SET_ACTIVITY":
const {activity, pid} = args; // translate given parameters into what discord dispatch expects
if (!activity) {
// Activity clear
socket.send?.({
cmd,
data: null,
evt: null,
nonce
});
return this.emit("activity", {
activity: null,
pid,
socketId: socket.socketId.toString()
});
}
const {buttons, timestamps, instance} = activity;
socket.lastPid = pid ?? socket.lastPid;
const metadata = {};
const extra = {};
if (buttons) {
// map buttons into expected metadata
metadata.button_urls = buttons.map((x) => x.url);
extra.buttons = buttons.map((x) => x.label);
}
if (timestamps)
for (const x in timestamps) {
// translate s -> ms timestamps
if (Date.now().toString().length - timestamps[x].toString().length > 2)
timestamps[x] = Math.floor(1000 * timestamps[x]);
}
this.emit("activity", {
activity: {
application_id: socket.clientId,
type: 0,
metadata,
flags: instance ? 1 << 0 : 0,
...activity,
...extra
},
pid,
socketId: socket.socketId.toString()
});
socket.send?.({
cmd,
data: {
...activity,
name: "",
application_id: socket.clientId,
type: 0
},
evt: null,
nonce
});
break;
case "GUILD_TEMPLATE_BROWSER":
case "INVITE_BROWSER":
const {code} = args;
const isInvite = cmd === "INVITE_BROWSER";
const callback = (isValid = true) => {
socket.send({
cmd,
data: isValid
? {code}
: {
code: isInvite ? 4011 : 4017,
message: `Invalid ${isInvite ? "invite" : "guild template"} id: ${code}`
},
evt: isValid ? null : "ERROR",
nonce
});
};
this.emit(isInvite ? "invite" : "guild-template", code, callback);
break;
case "DEEP_LINK":
this.emit("link", args.params);
break;
}
}
}
module.exports = RPCServer;

View file

@ -1,271 +0,0 @@
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 {join} = require("path");
const {platform, env} = require("process");
const {unlinkSync} = require("fs");
const {createServer, createConnection} = require("net");
const SOCKET_PATH =
platform === "win32"
? "\\\\?\\pipe\\discord-ipc"
: join(env.XDG_RUNTIME_DIR || env.TMPDIR || env.TMP || env.TEMP || "/tmp", "discord-ipc");
// enums for various constants
const Types = {
// types of packets
HANDSHAKE: 0,
FRAME: 1,
CLOSE: 2,
PING: 3,
PONG: 4
};
const CloseCodes = {
// codes for closures
CLOSE_NORMAL: 1000,
CLOSE_UNSUPPORTED: 1003,
CLOSE_ABNORMAL: 1006
};
const ErrorCodes = {
// codes for errors
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)), // errored
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(handers) {
return new Promise(async (res) => {
this.handlers = handers;
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 = parseInt(params.v ?? 1);
const clientId = params.client_id ?? "";
// encoding is always json for ipc
socket.close = (code = CloseCodes.CLOSE_NORMAL, message = "") => {
socket.end(
encode(Types.CLOSE, {
code,
message
})
);
socket.destroy();
};
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);
this.handlers.close(socket);
});
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.handlers.connection(socket);
});
}
onMessage(socket, msg) {
log("message", msg);
this.handlers.message(socket, msg);
}
}
module.exports = IPCServer;

View file

@ -1,135 +0,0 @@
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 {WebSocketServer} = require("ws");
const {createServer} = require("http");
const {parse} = require("querystring");
const portRange = [6463, 6472]; // ports available/possible: 6463-6472
class WSServer {
constructor(handlers) {
return (async () => {
this.handlers = handlers;
this.onConnection = this.onConnection.bind(this);
this.onMessage = this.onMessage.bind(this);
let port = portRange[0];
let http, wss;
while (port <= portRange[1]) {
log("trying port", port);
if (
await new Promise((res) => {
http = createServer();
http.on("error", (e) => {
// log('http error', e);
if (e.code === "EADDRINUSE") {
log(port, "in use!");
res(false);
}
});
wss = new 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(true);
});
})
)
break;
port++;
}
return this;
})();
}
onConnection(socket, req) {
const params = parse(req.url.split("?")[1]);
const ver = parseInt(params.v ?? 1);
const encoding = params.encoding ?? "json"; // json | etf (erlpack)
const clientId = params.client_id ?? "";
const origin = req.headers.origin ?? "";
log(`new connection! origin:`, origin, JSON.parse(JSON.stringify(params)));
if (
origin !== "" &&
![
"https://discord.com",
"https://ptb.discord.com",
"https://canary.discord.com",
"https://replugged.dev"
].includes(origin)
) {
log("disallowed origin", 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, r);
this.handlers.close(socket);
});
socket.on("message", this.onMessage.bind(this, socket));
socket._send = socket.send;
socket.send = (msg) => {
log("sending", msg);
socket._send(JSON.stringify(msg));
};
this.handlers.connection(socket);
}
onMessage(socket, msg) {
log("message", JSON.parse(msg));
this.handlers.message(socket, JSON.parse(msg));
}
}
module.exports = WSServer;

View file

@ -149,7 +149,7 @@ export function setup(): void {
trayIcon: "default",
doneSetup: false,
clientName: "ArmCord",
customIcon: path.join(__dirname, "../", "/assets/desktop.png")
customIcon: path.join(import.meta.dirname, "../", "/assets/desktop.png")
};
setConfigBulk({
...defaults

View file

@ -30,15 +30,15 @@ export async function getLang(object: string): Promise<string> {
if (language.length == 2) {
language = `${language}-${language.toUpperCase()}`;
}
let langPath = path.join(__dirname, "../", `/assets/lang/${language}.json`);
let langPath = path.join(import.meta.dirname, "../", `/assets/lang/${language}.json`);
if (!fs.existsSync(langPath)) {
langPath = path.join(__dirname, "../", "/assets/lang/en-US.json");
langPath = path.join(import.meta.dirname, "../", "/assets/lang/en-US.json");
}
let rawdata = fs.readFileSync(langPath, "utf-8");
let parsed = JSON.parse(rawdata);
if (parsed[object] == undefined) {
console.log(`${object} is undefined in ${language}`);
langPath = path.join(__dirname, "../", "/assets/lang/en-US.json");
langPath = path.join(import.meta.dirname, "../", "/assets/lang/en-US.json");
rawdata = fs.readFileSync(langPath, "utf-8");
parsed = JSON.parse(rawdata);
return parsed[object];

View file

@ -1,6 +1,6 @@
import {app} from "electron";
export const packageVersion = require("../package.json").version;
export const packageVersion = "3.2.0";
export function getVersion(): string {
return packageVersion;

View file

@ -3,8 +3,8 @@ import extract from "extract-zip";
import path from "path";
import {getConfig} from "../../common/config";
import fs from "fs";
import util from "util";
const streamPipeline = util.promisify(require("stream").pipeline);
import {pipeline} from "stream";
const streamPipeline = pipeline;
async function updateModBundle(): Promise<void> {
if ((await getConfig("noBundleUpdates")) == undefined ?? false) {
try {
@ -53,7 +53,7 @@ export function updateModInstallState() {
modInstallState = "modDownload";
updateModBundle();
import("./plugin");
import("./plugin.js");
modInstallState = "done";
}
@ -63,7 +63,7 @@ export async function installModLoader(): Promise<void> {
modInstallState = "none";
fs.rmSync(`${app.getPath("userData")}/plugins/loader`, {recursive: true, force: true});
import("./plugin");
import("./plugin.js");
console.log("[Mod loader] Skipping");
return;

View file

@ -39,7 +39,7 @@ export function registerIpc(): void {
break;
case "win32":
if (pingCount > 0) {
let image = nativeImage.createFromPath(path.join(__dirname, "../", `/assets/ping.png`));
let image = nativeImage.createFromPath(path.join(import.meta.dirname, "../", `/assets/ping.png`));
mainWindow.setOverlayIcon(image, "badgeCount");
} else {
mainWindow.setOverlayIcon(null, "badgeCount");

View file

@ -1,5 +1,5 @@
import {contextBridge, ipcRenderer} from "electron";
import {injectTitlebar} from "./titlebar";
import {injectTitlebar} from "./titlebar.mjs";
const CANCEL_ID = "desktop-capturer-selection__cancel";
const desktopCapturer = {
getSources: (opts: any) => ipcRenderer.invoke("DESKTOP_CAPTURER_GET_SOURCES", opts)

View file

@ -2,7 +2,7 @@
//original https://github.com/electron/electron/issues/16513#issuecomment-602070250
import fs from "fs";
import path from "path";
import {addScript, addStyle} from "../../common/dom";
import {addScript, addStyle} from "../../common/dom.js";
const CANCEL_ID = "desktop-capturer-selection__cancel";
@ -48,7 +48,7 @@ window.navigator.mediaDevices.getDisplayMedia = () => new Promise(async (resolve
document.addEventListener("DOMContentLoaded", function () {
addScript(screenShareJS);
const screenshareCss = path.join(__dirname, "../", "/content/css/screenshare.css");
const screenshareCss = path.join(import.meta.dirname, "../", "/content/css/screenshare.css");
addStyle(fs.readFileSync(screenshareCss, "utf8"));
console.log("Capturer injected.");
});

View file

@ -1,9 +1,9 @@
import {addStyle} from "../../common/dom";
import {addStyle} from "../../common/dom.js";
import * as fs from "fs";
import * as path from "path";
export function injectMobileStuff(): void {
document.addEventListener("DOMContentLoaded", function () {
const mobileCSS = path.join(__dirname, "../", "/content/css/mobile.css");
const mobileCSS = path.join(import.meta.dirname, "../", "/content/css/mobile.css");
addStyle(fs.readFileSync(mobileCSS, "utf8"));
// TO-DO: clicking on the logo, or additional button triggers ESC button to move around the UI quicker
// var logo = document.getElementById("window-title");

View file

@ -1,20 +1,20 @@
import "./bridge";
import "./optimizer";
import "./settings";
import "./bridge.mjs";
import "./optimizer.mjs";
import "./settings.mjs";
import {ipcRenderer} from "electron";
import * as fs from "fs";
import * as path from "path";
import {injectMobileStuff} from "./mobile";
import {fixTitlebar, injectTitlebar} from "./titlebar";
import {injectSettings} from "./settings";
import {addStyle, addScript} from "../../common/dom";
import {sleep} from "../../common/sleep";
import {injectMobileStuff} from "./mobile.mjs";
import {fixTitlebar, injectTitlebar} from "./titlebar.mjs";
import {injectSettings} from "./settings.mjs";
import {addStyle, addScript} from "../../common/dom.js";
import {sleep} from "../../common/sleep.js";
window.localStorage.setItem("hideNag", "true");
if (ipcRenderer.sendSync("legacyCapturer")) {
console.warn("Using legacy capturer module");
import("./capturer");
import("./capturer.mjs");
}
const version = ipcRenderer.sendSync("displayVersion");
@ -57,10 +57,10 @@ sleep(5000).then(async () => {
})();
`);
if (ipcRenderer.sendSync("disableAutogain")) {
addScript(fs.readFileSync(path.join(__dirname, "../", "../", "/content/js/disableAutogain.js"), "utf8"));
addScript(fs.readFileSync(path.join(import.meta.dirname, "../", "/content/js/disableAutogain.js"), "utf8"));
}
addScript(fs.readFileSync(path.join(__dirname, "../", "../", "/content/js/rpc.js"), "utf8"));
const cssPath = path.join(__dirname, "../", "../", "/content/css/discord.css");
addScript(fs.readFileSync(path.join(import.meta.dirname, "../", "/content/js/rpc.js"), "utf8"));
const cssPath = path.join(import.meta.dirname, "../", "/content/css/discord.css");
addStyle(fs.readFileSync(cssPath, "utf8"));
await updateLang();
});

View file

@ -1,15 +1,21 @@
import * as path from "path";
import * as fs from "fs";
import {addStyle} from "../../common/dom";
import {addStyle} from "../../common/dom.js";
import {WebviewTag} from "electron";
var webview = `<webview src="${path.join(
"file://",
__dirname,
import.meta.dirname,
"../",
"../",
"/settings/settings.html"
)}" preload="${path.join("file://", __dirname, "../", "../", "/settings/preload.js")}" id="inAppSettings"></webview>`;
)}" preload="${path.join(
"file://",
import.meta.dirname,
"../",
"../",
"/settings/preload.mjs"
)}" id="inAppSettings"></webview>`;
export function injectSettings() {
document.getElementById("webviewSettingsContainer")!.innerHTML = webview;
@ -17,7 +23,7 @@ export function injectSettings() {
}
document.addEventListener("DOMContentLoaded", function (_event) {
const settingsCssPath = path.join(__dirname, "../", "/content/css/inAppSettings.css");
const settingsCssPath = path.join(import.meta.dirname, "../", "/content/css/inAppSettings.css");
addStyle(fs.readFileSync(settingsCssPath, "utf8"));
const webview = document.querySelector("webview") as WebviewTag;
webview.addEventListener("console-message", (e) => {

View file

@ -1,5 +1,5 @@
import {ipcRenderer} from "electron";
import {addStyle} from "../../common/dom";
import {addStyle} from "../../common/dom.js";
import * as fs from "fs";
import * as path from "path";
import os from "os";
@ -21,8 +21,8 @@ export function injectTitlebar(): void {
} else {
document.getElementById("app-mount")!.prepend(elem);
}
const titlebarcssPath = path.join(__dirname, "../", "../", "/content/css/titlebar.css");
const wordmarkcssPath = path.join(__dirname, "../", "../", "/content/css/logos.css");
const titlebarcssPath = path.join(import.meta.dirname, "../", "../", "/content/css/titlebar.css");
const wordmarkcssPath = path.join(import.meta.dirname, "../", "../", "/content/css/logos.css");
addStyle(fs.readFileSync(titlebarcssPath, "utf8"));
addStyle(fs.readFileSync(wordmarkcssPath, "utf8"));
document.body.setAttribute("customTitlebar", "");

View file

@ -1,6 +1,6 @@
import {BrowserWindow, MessageBoxOptions, desktopCapturer, dialog, ipcMain, session} from "electron";
import path from "path";
import {iconPath} from "../../main";
import {iconPath} from "../../main.js";
let capturerWindow: BrowserWindow;
function showAudioDialog(): boolean {
const options: MessageBoxOptions = {
@ -46,7 +46,7 @@ function registerCustomHandler(): void {
webPreferences: {
sandbox: false,
spellcheck: false,
preload: path.join(__dirname, "preload.js")
preload: path.join(import.meta.dirname, "preload.js")
}
});
ipcMain.once("selectScreenshareSource", (_event, id, name) => {
@ -62,7 +62,7 @@ function registerCustomHandler(): void {
callback({video: result});
}
});
capturerWindow.loadURL(`file://${__dirname}/picker.html`);
capturerWindow.loadURL(`file://${import.meta.dirname}/picker.html`);
capturerWindow.webContents.send("getSources", sources);
}
});

View file

@ -9,6 +9,7 @@ import {setMenu} from "./menu";
import * as fs from "fs";
import contextMenu from "electron-context-menu";
import os from "os";
import RPCServer from "arrpc";
import {tray} from "../tray";
import {iconPath} from "../main";
import {getConfig, setConfig, firstRun} from "../common/config";
@ -129,7 +130,7 @@ async function doAfterDefiningTheWindow(): Promise<void> {
});
if ((await getConfig("useLegacyCapturer")) == false) {
console.log("Starting screenshare module...");
import("./screenshare/main");
import("./screenshare/main.js");
}
mainWindow.webContents.session.webRequest.onBeforeRequest(
@ -258,14 +259,18 @@ async function doAfterDefiningTheWindow(): Promise<void> {
mainWindow.webContents.executeJavaScript(`document.body.removeAttribute("isMaximized");`);
});
if ((await getConfig("inviteWebsocket")) == true) {
require("arrpc");
//await startServer();
const server = await new RPCServer();
server.on("activity", (data: string) => mainWindow.webContents.send("rpc", data));
server.on("invite", (code: string) => {
console.log(code);
createInviteWindow(code);
});
}
if (firstRun) {
mainWindow.close();
}
//loadURL broke for no good reason after E28
mainWindow.loadFile(`${__dirname}/../splash/redirect.html`);
mainWindow.loadFile(`${import.meta.dirname}/../splash/redirect.html`);
if (await getConfig("skipSplash")) {
mainWindow.show();
@ -287,7 +292,7 @@ export async function createCustomWindow(): Promise<void> {
webPreferences: {
webviewTag: true,
sandbox: false,
preload: path.join(__dirname, "preload/preload.js"),
preload: path.join(import.meta.dirname, "preload/preload.mjs"),
spellcheck: await getConfig("spellcheck")
}
});
@ -309,7 +314,7 @@ export async function createNativeWindow(): Promise<void> {
webPreferences: {
webviewTag: true,
sandbox: false,
preload: path.join(__dirname, "preload/preload.js"),
preload: path.join(import.meta.dirname, "preload/preload.mjs"),
spellcheck: await getConfig("spellcheck")
}
});
@ -331,7 +336,7 @@ export async function createTransparentWindow(): Promise<void> {
webPreferences: {
sandbox: false,
webviewTag: true,
preload: path.join(__dirname, "preload/preload.js"),
preload: path.join(import.meta.dirname, "preload/preload.mjs"),
spellcheck: await getConfig("spellcheck")
}
});

View file

@ -86,7 +86,7 @@ if (!app.requestSingleInstanceLock() && getConfigSync("multiInstance") == (false
if ((await getConfig("customIcon")) !== undefined ?? null) {
iconPath = await getConfig("customIcon");
} else {
iconPath = path.join(__dirname, "../", "/assets/desktop.png");
iconPath = path.join(import.meta.dirname, "../", "/assets/desktop.png");
}
async function init(): Promise<void> {
if ((await getConfig("skipSplash")) == false) {

View file

@ -24,11 +24,11 @@ export function createSettingsWindow(): void {
autoHideMenuBar: true,
webPreferences: {
sandbox: false,
preload: path.join(__dirname, "preload.js")
preload: path.join(import.meta.dirname, "preload.mjs")
}
});
async function settingsLoadPage(): Promise<void> {
settingsWindow.loadURL(`file://${__dirname}/settings.html`);
settingsWindow.loadURL(`file://${import.meta.dirname}/settings.html`);
}
const userDataPath = app.getPath("userData");
const themesFolder = `${userDataPath}/themes/`;

View file

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<style>
@import url("../content/css/settings.css");
@import url("./settings.css");
</style>
</head>

View file

@ -17,7 +17,7 @@ export function createSetupWindow(): void {
webPreferences: {
sandbox: false,
spellcheck: false,
preload: path.join(__dirname, "preload.js")
preload: path.join(import.meta.dirname, "preload.js")
}
});
ipcMain.on("saveSettings", (_event, args: Settings) => {
@ -38,5 +38,5 @@ export function createSetupWindow(): void {
app.quit();
});
});
setupWindow.loadURL(`file://${__dirname}/setup.html`);
setupWindow.loadURL(`file://${import.meta.dirname}/setup.html`);
}

View file

@ -1,5 +1,5 @@
import {contextBridge, ipcRenderer} from "electron";
import {injectTitlebar} from "../discord/preload/titlebar";
import {injectTitlebar} from "../discord/preload/titlebar.mjs";
injectTitlebar();
contextBridge.exposeInMainWorld("armcordinternal", {

View file

@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ArmCord Setup</title>
<style>
@import url("../content/css/setup.css");
@import url("./setup.css");
</style>
</head>

View file

@ -16,8 +16,8 @@ export async function createSplashWindow(): Promise<void> {
autoHideMenuBar: true,
webPreferences: {
sandbox: false,
preload: path.join(__dirname, "preload.js")
preload: path.join(import.meta.dirname, "preload.mjs")
}
});
splashWindow.loadFile(path.join(__dirname, "splash.html"));
splashWindow.loadFile(path.join(import.meta.dirname, "splash.html"));
}

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<title>ArmCord</title>
<style>
@import url("../content/css/splash.css");
@import url("./splash.css");
</style>
<script>
window.onbeforeunload = function () {

View file

@ -107,7 +107,7 @@ export function createTManagerWindow(): void {
autoHideMenuBar: true,
webPreferences: {
sandbox: false,
preload: path.join(__dirname, "preload.js")
preload: path.join(import.meta.dirname, "preload.mjs")
}
});
//setWindowHandler doesn't work for some reason
@ -124,7 +124,7 @@ export function createTManagerWindow(): void {
});
async function managerLoadPage(): Promise<void> {
themeWindow.loadFile(`${__dirname}/manager.html`);
themeWindow.loadFile(`${import.meta.dirname}/manager.html`);
}
const userDataPath = app.getPath("userData");
const themesFolder = `${userDataPath}/themes/`;

View file

@ -12,7 +12,7 @@ app.whenReady().then(async () => {
if ((await getConfig("trayIcon")) != "default") {
trayIcon = await getConfig("trayIcon");
}
let trayPath = nativeImage.createFromPath(path.join(__dirname, "../", `/assets/${trayIcon}.png`));
let trayPath = nativeImage.createFromPath(path.join(import.meta.dirname, "../", `/assets/${trayIcon}.png`));
let trayVerIcon;
trayVerIcon = function () {
if (process.platform == "win32") {

1
src/types/arrpc.d.ts vendored Normal file
View file

@ -0,0 +1 @@
declare module "arrpc";

View file

@ -14,11 +14,10 @@
"noFallthroughCasesInSwitch": true, // Prevents accidentally forgetting to break every switch case. Of course, if you know what you're doing, feel free to add a @ts-ignore, which also signals that it's not a mistake.
"forceConsistentCasingInFileNames": true, // Make import paths case-sensitive. "./tEst" is no longer the same as "./test".
"esModuleInterop": true, // Enables compatibility with Node.js' module system since the entire export can be whatever you want. allowSyntheticDefaultImports doesn't address runtime issues and is made redundant by this setting.
"resolveJsonModule": true, // Allows you to import JSON files just like how you can require() them. Do note that if you're accessing any JSON files outside of src, it'll mess up dist.
"lib": ["ES2020", "DOM"], // Specifies what common libraries you have access to. If you're working in Node.js, you'll want to leave out the DOM library. But do make sure to include "@types/node" because otherwise, variables like "console" won't be defined.
"lib": ["ES2022", "DOM"], // Specifies what common libraries you have access to. If you're working in Node.js, you'll want to leave out the DOM library. But do make sure to include "@types/node" because otherwise, variables like "console" won't be defined.
"noUnusedLocals": true, // Warns you if you have unused variables.
// Output //
"module": "CommonJS", // Compiles ES6 imports to require() syntax.
"module": "ES2022", // Compiles ES6 imports to require() syntax.
"removeComments": false,
"sourceMap": true, // Used for displaying the original source when debugging in webpack. Allows you to set breakpoints directly on TypeScript code for VSCode's debugger.
@ -27,7 +26,7 @@
"declarationMap": false, // Allows the user to go to the source file when hitting a go-to-implementation key like F12 in VSCode for example.
//"declarationDir": "typings", // declarationDir allows you to separate the compiled code from the declaration files, used in conjunction with package.json's "types" property.
// Web Compatibility //
"target": "ES2020", // ES2017 supports async/await, reducing the amount of compiled code, especially for async-heavy projects. ES2020 is from the Node 14 base (https://github.com/tsconfig/bases/blob/master/bases/node14.json)
"target": "ES2022", // ES2017 supports async/await, reducing the amount of compiled code, especially for async-heavy projects. ES2020 is from the Node 14 base (https://github.com/tsconfig/bases/blob/master/bases/node14.json)
"downlevelIteration": false, // This flag adds extra support when targeting ES3, but adds extra bloat otherwise.
"importHelpers": false // Reduce the amount of bloat that comes from downlevelIteration (when polyfills are redeclared).
}