mirror of
https://github.com/smartfrigde/armcord.git
synced 2024-08-14 23:56:58 +00:00
feat: rewrite to esm
This commit is contained in:
parent
419cb8eb4a
commit
0487507bc4
48 changed files with 121 additions and 51054 deletions
11
package.json
11
package.json
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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;
|
|
@ -1,3 +0,0 @@
|
|||
const win32 = require("./win32");
|
||||
const linux = require("./linux");
|
||||
module.exports = {win32, linux};
|
|
@ -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};
|
|
@ -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};
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
|
@ -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.");
|
||||
});
|
|
@ -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");
|
|
@ -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();
|
||||
});
|
|
@ -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) => {
|
|
@ -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", "");
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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/`;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<style>
|
||||
@import url("../content/css/settings.css");
|
||||
@import url("./settings.css");
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -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`);
|
||||
}
|
||||
|
|
|
@ -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", {
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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/`;
|
||||
|
|
|
@ -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
1
src/types/arrpc.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
declare module "arrpc";
|
|
@ -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).
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue