@nyabsi/minimal-discord-rpc 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/structures/client.d.ts +103 -0
- package/dist/structures/client.js +135 -0
- package/dist/structures/eventEmitter.d.ts +5 -0
- package/dist/structures/eventEmitter.js +28 -0
- package/dist/structures/ipcTransport.d.ts +21 -0
- package/dist/structures/ipcTransport.js +204 -0
- package/package.json +1 -1
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Client, ActivityType } from "./structures/client";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ActivityType = exports.Client = void 0;
|
|
4
|
+
var client_1 = require("./structures/client");
|
|
5
|
+
Object.defineProperty(exports, "Client", { enumerable: true, get: function () { return client_1.Client; } });
|
|
6
|
+
Object.defineProperty(exports, "ActivityType", { enumerable: true, get: function () { return client_1.ActivityType; } });
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { EventEmitter } from "./eventEmitter";
|
|
2
|
+
interface ClientOptions {
|
|
3
|
+
clientId: string;
|
|
4
|
+
}
|
|
5
|
+
export declare enum ActivityType {
|
|
6
|
+
/**
|
|
7
|
+
* Playing {game}
|
|
8
|
+
*/
|
|
9
|
+
Playing = 0,
|
|
10
|
+
/**
|
|
11
|
+
* Listening to {name}
|
|
12
|
+
*/
|
|
13
|
+
Listening = 2,
|
|
14
|
+
/**
|
|
15
|
+
* Watching {details}
|
|
16
|
+
*/
|
|
17
|
+
Watching = 3,
|
|
18
|
+
/**
|
|
19
|
+
* Competing in {name}
|
|
20
|
+
*/
|
|
21
|
+
Competing = 5
|
|
22
|
+
}
|
|
23
|
+
interface ActivityTimestamps {
|
|
24
|
+
/**
|
|
25
|
+
* Unix time (in milliseconds) of when the activity started
|
|
26
|
+
* Must be between 0 and 2147483647000
|
|
27
|
+
*/
|
|
28
|
+
start?: number | Date;
|
|
29
|
+
/**
|
|
30
|
+
* Unix time (in milliseconds) of when the activity ends
|
|
31
|
+
* Must be between 0 and 2147483647000
|
|
32
|
+
*/
|
|
33
|
+
end?: number | Date;
|
|
34
|
+
}
|
|
35
|
+
interface ActivityAssets {
|
|
36
|
+
/**
|
|
37
|
+
* Main icon, can be an asset name or a URL (1-128 characters)
|
|
38
|
+
* If not specified, the client icon will be used
|
|
39
|
+
*/
|
|
40
|
+
large_image?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Hover text for the main icon (2-128 characters)
|
|
43
|
+
*/
|
|
44
|
+
large_text?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Small icon, can be an asset name or a URL (1-128 characters)
|
|
47
|
+
*/
|
|
48
|
+
small_image?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Hover text for the small icon (2-128 characters)
|
|
51
|
+
*/
|
|
52
|
+
small_text?: string;
|
|
53
|
+
}
|
|
54
|
+
interface ActivityButton {
|
|
55
|
+
/**
|
|
56
|
+
* The text shown on the button (1-32 characters)
|
|
57
|
+
*/
|
|
58
|
+
label: string;
|
|
59
|
+
/**
|
|
60
|
+
* The url opened when clicking the button (1-512 characters)
|
|
61
|
+
*/
|
|
62
|
+
url: string;
|
|
63
|
+
}
|
|
64
|
+
interface Activity {
|
|
65
|
+
/**
|
|
66
|
+
* The player's current party status (2-128 characters)
|
|
67
|
+
*/
|
|
68
|
+
state?: string;
|
|
69
|
+
/**
|
|
70
|
+
* What the player is currently doing (2-128 characters)
|
|
71
|
+
*/
|
|
72
|
+
details?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Create elapsed/remaining timestamps on a player's profile
|
|
75
|
+
*/
|
|
76
|
+
timestamps?: ActivityTimestamps;
|
|
77
|
+
/**
|
|
78
|
+
* Internal - Undocumented
|
|
79
|
+
*/
|
|
80
|
+
flags?: number;
|
|
81
|
+
/**
|
|
82
|
+
* Assets to display on the player's profile
|
|
83
|
+
*/
|
|
84
|
+
assets?: ActivityAssets;
|
|
85
|
+
/**
|
|
86
|
+
* List of interactive buttons
|
|
87
|
+
*/
|
|
88
|
+
buttons?: ActivityButton[];
|
|
89
|
+
/**
|
|
90
|
+
* Default: ActivityType.Playing
|
|
91
|
+
*/
|
|
92
|
+
type?: ActivityType.Playing | ActivityType.Listening | ActivityType.Watching | ActivityType.Competing;
|
|
93
|
+
}
|
|
94
|
+
export declare class Client extends EventEmitter {
|
|
95
|
+
#private;
|
|
96
|
+
clientId: string;
|
|
97
|
+
constructor(options: ClientOptions);
|
|
98
|
+
login(): Promise<void>;
|
|
99
|
+
setActivity(activity: Activity, pid?: number): Promise<void>;
|
|
100
|
+
clearActivity(pid?: number): Promise<void>;
|
|
101
|
+
destroy(): Promise<void>;
|
|
102
|
+
}
|
|
103
|
+
export {};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
+
};
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
var _Client_instances, _Client_transport, _Client_request;
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.Client = exports.ActivityType = void 0;
|
|
19
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
20
|
+
const eventEmitter_1 = require("./eventEmitter");
|
|
21
|
+
const ipcTransport_1 = require("./ipcTransport");
|
|
22
|
+
var ActivityType;
|
|
23
|
+
(function (ActivityType) {
|
|
24
|
+
/**
|
|
25
|
+
* Playing {game}
|
|
26
|
+
*/
|
|
27
|
+
ActivityType[ActivityType["Playing"] = 0] = "Playing";
|
|
28
|
+
/**
|
|
29
|
+
* Listening to {name}
|
|
30
|
+
*/
|
|
31
|
+
ActivityType[ActivityType["Listening"] = 2] = "Listening";
|
|
32
|
+
/**
|
|
33
|
+
* Watching {details}
|
|
34
|
+
*/
|
|
35
|
+
ActivityType[ActivityType["Watching"] = 3] = "Watching";
|
|
36
|
+
/**
|
|
37
|
+
* Competing in {name}
|
|
38
|
+
*/
|
|
39
|
+
ActivityType[ActivityType["Competing"] = 5] = "Competing";
|
|
40
|
+
})(ActivityType || (exports.ActivityType = ActivityType = {}));
|
|
41
|
+
class Client extends eventEmitter_1.EventEmitter {
|
|
42
|
+
constructor(options) {
|
|
43
|
+
super();
|
|
44
|
+
_Client_instances.add(this);
|
|
45
|
+
Object.defineProperty(this, "clientId", {
|
|
46
|
+
enumerable: true,
|
|
47
|
+
configurable: true,
|
|
48
|
+
writable: true,
|
|
49
|
+
value: void 0
|
|
50
|
+
});
|
|
51
|
+
_Client_transport.set(this, void 0);
|
|
52
|
+
this.clientId = options.clientId;
|
|
53
|
+
__classPrivateFieldSet(this, _Client_transport, new ipcTransport_1.IpcTransport(this), "f");
|
|
54
|
+
__classPrivateFieldGet(this, _Client_transport, "f").on("message", (message) => {
|
|
55
|
+
if (message.cmd === "DISPATCH" && message.evt === "READY") {
|
|
56
|
+
this.emit("ready");
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
this.emit(message.evt, message.data);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// LOGIN
|
|
64
|
+
async login() {
|
|
65
|
+
await __classPrivateFieldGet(this, _Client_transport, "f").connect();
|
|
66
|
+
}
|
|
67
|
+
async setActivity(activity, pid) {
|
|
68
|
+
const cleaned = {};
|
|
69
|
+
if (activity.state)
|
|
70
|
+
cleaned.state = activity.state;
|
|
71
|
+
if (activity.details)
|
|
72
|
+
cleaned.details = activity.details;
|
|
73
|
+
if (activity.timestamps && Object.entries(activity.timestamps).length > 0) {
|
|
74
|
+
cleaned.timestamps = {};
|
|
75
|
+
if (activity.timestamps?.start)
|
|
76
|
+
cleaned.timestamps.start = activity.timestamps.start;
|
|
77
|
+
if (activity.timestamps?.end)
|
|
78
|
+
cleaned.timestamps.end = activity.timestamps.end;
|
|
79
|
+
}
|
|
80
|
+
if (activity.flags)
|
|
81
|
+
cleaned.flags = activity.flags;
|
|
82
|
+
if (activity.assets && Object.entries(activity.assets).length > 0) {
|
|
83
|
+
cleaned.assets = {};
|
|
84
|
+
if (activity.assets?.large_image)
|
|
85
|
+
cleaned.assets.large_image = activity.assets.large_image;
|
|
86
|
+
if (activity.assets?.large_text)
|
|
87
|
+
cleaned.assets.large_text = activity.assets.large_text;
|
|
88
|
+
if (activity.assets?.small_image)
|
|
89
|
+
cleaned.assets.small_image = activity.assets.small_image;
|
|
90
|
+
if (activity.assets?.small_text)
|
|
91
|
+
cleaned.assets.small_text = activity.assets.small_text;
|
|
92
|
+
}
|
|
93
|
+
if (activity.buttons && activity.buttons.length > 0) {
|
|
94
|
+
cleaned.buttons = [];
|
|
95
|
+
for (const button of activity.buttons) {
|
|
96
|
+
cleaned.buttons.push({
|
|
97
|
+
label: button.label,
|
|
98
|
+
url: button.url,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (activity.type) {
|
|
103
|
+
cleaned.type = activity.type;
|
|
104
|
+
if ([
|
|
105
|
+
ActivityType.Playing,
|
|
106
|
+
ActivityType.Listening,
|
|
107
|
+
ActivityType.Watching,
|
|
108
|
+
ActivityType.Competing,
|
|
109
|
+
].includes(cleaned.type))
|
|
110
|
+
cleaned.type = ActivityType.Playing;
|
|
111
|
+
}
|
|
112
|
+
await __classPrivateFieldGet(this, _Client_instances, "m", _Client_request).call(this, "SET_ACTIVITY", {
|
|
113
|
+
pid: (pid ?? process) ? (process.pid ?? 0) : 0,
|
|
114
|
+
activity,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
async clearActivity(pid) {
|
|
118
|
+
await __classPrivateFieldGet(this, _Client_instances, "m", _Client_request).call(this, "CLEAR_ACTIVITY", {
|
|
119
|
+
pid: (pid ?? process) ? (process.pid ?? 0) : 0,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async destroy() {
|
|
123
|
+
await __classPrivateFieldGet(this, _Client_transport, "f").close();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
exports.Client = Client;
|
|
127
|
+
_Client_transport = new WeakMap(), _Client_instances = new WeakSet(), _Client_request =
|
|
128
|
+
// RICH PRESENCE
|
|
129
|
+
async function _Client_request(cmd, args) {
|
|
130
|
+
__classPrivateFieldGet(this, _Client_transport, "f").send(ipcTransport_1.IPC_OPERATION.FRAME, {
|
|
131
|
+
cmd,
|
|
132
|
+
args,
|
|
133
|
+
nonce: node_crypto_1.default.randomUUID(),
|
|
134
|
+
});
|
|
135
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
+
};
|
|
7
|
+
var _EventEmitter_listeners;
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.EventEmitter = void 0;
|
|
10
|
+
class EventEmitter {
|
|
11
|
+
constructor() {
|
|
12
|
+
_EventEmitter_listeners.set(this, new Map());
|
|
13
|
+
}
|
|
14
|
+
on(event, callback) {
|
|
15
|
+
const eventListeners = __classPrivateFieldGet(this, _EventEmitter_listeners, "f").get(event);
|
|
16
|
+
if (!eventListeners)
|
|
17
|
+
__classPrivateFieldGet(this, _EventEmitter_listeners, "f").set(event, [callback]);
|
|
18
|
+
else
|
|
19
|
+
eventListeners?.push(callback);
|
|
20
|
+
}
|
|
21
|
+
emit(event, ...args) {
|
|
22
|
+
for (const listener of __classPrivateFieldGet(this, _EventEmitter_listeners, "f").get(event) ?? []) {
|
|
23
|
+
listener(...args);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.EventEmitter = EventEmitter;
|
|
28
|
+
_EventEmitter_listeners = new WeakMap();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Client } from "./client";
|
|
2
|
+
import net from "node:net";
|
|
3
|
+
import { EventEmitter } from "./eventEmitter";
|
|
4
|
+
export declare enum IPC_OPERATION {
|
|
5
|
+
HANDSHAKE = 0,
|
|
6
|
+
FRAME = 1,
|
|
7
|
+
CLOSE = 2,
|
|
8
|
+
PING = 3,
|
|
9
|
+
PONG = 4
|
|
10
|
+
}
|
|
11
|
+
export declare class IpcTransport extends EventEmitter {
|
|
12
|
+
client: Client;
|
|
13
|
+
socket?: net.Socket;
|
|
14
|
+
constructor(client: Client);
|
|
15
|
+
get isConnected(): boolean;
|
|
16
|
+
getSocket(): Promise<net.Socket>;
|
|
17
|
+
connect(): Promise<void>;
|
|
18
|
+
send(operation?: IPC_OPERATION, payload?: any): void;
|
|
19
|
+
ping(): void;
|
|
20
|
+
close(): Promise<void>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.IpcTransport = exports.IPC_OPERATION = void 0;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const node_net_1 = __importDefault(require("node:net"));
|
|
10
|
+
const eventEmitter_1 = require("./eventEmitter");
|
|
11
|
+
var IPC_OPERATION;
|
|
12
|
+
(function (IPC_OPERATION) {
|
|
13
|
+
IPC_OPERATION[IPC_OPERATION["HANDSHAKE"] = 0] = "HANDSHAKE";
|
|
14
|
+
IPC_OPERATION[IPC_OPERATION["FRAME"] = 1] = "FRAME";
|
|
15
|
+
IPC_OPERATION[IPC_OPERATION["CLOSE"] = 2] = "CLOSE";
|
|
16
|
+
IPC_OPERATION[IPC_OPERATION["PING"] = 3] = "PING";
|
|
17
|
+
IPC_OPERATION[IPC_OPERATION["PONG"] = 4] = "PONG";
|
|
18
|
+
})(IPC_OPERATION || (exports.IPC_OPERATION = IPC_OPERATION = {}));
|
|
19
|
+
const pathList = [
|
|
20
|
+
{
|
|
21
|
+
platform: ["win32"],
|
|
22
|
+
format: (id) => `\\\\?\\pipe\\discord-ipc-${id}`,
|
|
23
|
+
},
|
|
24
|
+
// MacOS and Linux
|
|
25
|
+
{
|
|
26
|
+
platform: ["darwin", "linux"],
|
|
27
|
+
format: (id) => {
|
|
28
|
+
const { env: { XDG_RUNTIME_DIR, TMPDIR, TMP, TEMP }, } = process;
|
|
29
|
+
const prefix = node_fs_1.default.realpathSync(XDG_RUNTIME_DIR ?? TMPDIR ?? TMP ?? TEMP ?? `${node_path_1.default.sep}tmp`);
|
|
30
|
+
return node_path_1.default.join(prefix, `discord-ipc-${id}`);
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
// Linux (Snap)
|
|
34
|
+
{
|
|
35
|
+
platform: ["linux"],
|
|
36
|
+
format: (id) => {
|
|
37
|
+
const { env: { XDG_RUNTIME_DIR, TMPDIR, TMP, TEMP }, } = process;
|
|
38
|
+
const prefix = node_fs_1.default.realpathSync(XDG_RUNTIME_DIR ?? TMPDIR ?? TMP ?? TEMP ?? `${node_path_1.default.sep}tmp`);
|
|
39
|
+
return node_path_1.default.join(prefix, "snap.discord", `discord-ipc-${id}`);
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
// Linux (Flatpak)
|
|
43
|
+
{
|
|
44
|
+
platform: ["linux"],
|
|
45
|
+
format: (id) => {
|
|
46
|
+
const { env: { XDG_RUNTIME_DIR, TMPDIR, TMP, TEMP }, } = process;
|
|
47
|
+
const prefix = node_fs_1.default.realpathSync(XDG_RUNTIME_DIR ?? TMPDIR ?? TMP ?? TEMP ?? `${node_path_1.default.sep}tmp`);
|
|
48
|
+
return node_path_1.default.join(prefix, "app", "com.discordapp.Discord", `discord-ipc-${id}`);
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
const createSocket = async (path) => {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const onError = () => {
|
|
55
|
+
socket.removeListener("conect", onConnect);
|
|
56
|
+
reject();
|
|
57
|
+
};
|
|
58
|
+
const onConnect = () => {
|
|
59
|
+
socket.removeListener("error", onError);
|
|
60
|
+
resolve(socket);
|
|
61
|
+
};
|
|
62
|
+
const socket = node_net_1.default.createConnection(path);
|
|
63
|
+
socket.once("connect", onConnect);
|
|
64
|
+
socket.once("error", onError);
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
class IpcTransport extends eventEmitter_1.EventEmitter {
|
|
68
|
+
constructor(client) {
|
|
69
|
+
super();
|
|
70
|
+
Object.defineProperty(this, "client", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
configurable: true,
|
|
73
|
+
writable: true,
|
|
74
|
+
value: void 0
|
|
75
|
+
});
|
|
76
|
+
Object.defineProperty(this, "socket", {
|
|
77
|
+
enumerable: true,
|
|
78
|
+
configurable: true,
|
|
79
|
+
writable: true,
|
|
80
|
+
value: void 0
|
|
81
|
+
});
|
|
82
|
+
this.client = client;
|
|
83
|
+
}
|
|
84
|
+
get isConnected() {
|
|
85
|
+
return this.socket !== undefined && this.socket.readyState === "open";
|
|
86
|
+
}
|
|
87
|
+
async getSocket() {
|
|
88
|
+
if (this.socket)
|
|
89
|
+
return this.socket;
|
|
90
|
+
for (const pat of pathList) {
|
|
91
|
+
const handleSocketId = async (id) => {
|
|
92
|
+
if (!pat.platform.includes(process.platform))
|
|
93
|
+
return;
|
|
94
|
+
const socketPath = pat.format(id);
|
|
95
|
+
if (process.platform !== "win32" &&
|
|
96
|
+
!node_fs_1.default.existsSync(node_path_1.default.dirname(socketPath)))
|
|
97
|
+
return;
|
|
98
|
+
const socket = await createSocket(socketPath).catch(() => undefined);
|
|
99
|
+
return socket;
|
|
100
|
+
};
|
|
101
|
+
for (let i = 0; i < 10; i++) {
|
|
102
|
+
const socket = await handleSocketId(i);
|
|
103
|
+
if (socket)
|
|
104
|
+
return socket;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
throw new Error("Could not connect");
|
|
108
|
+
}
|
|
109
|
+
async connect() {
|
|
110
|
+
if (!this.socket)
|
|
111
|
+
this.socket = await this.getSocket();
|
|
112
|
+
this.emit("open");
|
|
113
|
+
this.send(IPC_OPERATION.HANDSHAKE, {
|
|
114
|
+
v: 1,
|
|
115
|
+
client_id: this.client.clientId,
|
|
116
|
+
});
|
|
117
|
+
this.socket.on("readable", () => {
|
|
118
|
+
let data = Buffer.alloc(0);
|
|
119
|
+
while (true) {
|
|
120
|
+
if (!this.isConnected)
|
|
121
|
+
break;
|
|
122
|
+
const chunk = this.socket?.read();
|
|
123
|
+
if (!chunk)
|
|
124
|
+
break;
|
|
125
|
+
this.client.emit("debug", `SERVER => CLIENT | ${chunk
|
|
126
|
+
.toString("hex")
|
|
127
|
+
.match(/.{1,2}/g)
|
|
128
|
+
?.join(" ")
|
|
129
|
+
.toUpperCase()}`);
|
|
130
|
+
data = Buffer.concat([data, chunk]);
|
|
131
|
+
}
|
|
132
|
+
if (data.length < 8) {
|
|
133
|
+
if (data.length === 0)
|
|
134
|
+
return;
|
|
135
|
+
this.client.emit("debug", "SERVER => CLIENT | Malformed packet, invalid payload");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const op = data.readUInt32LE(0);
|
|
139
|
+
const length = data.readUInt32LE(4);
|
|
140
|
+
if (data.length !== length + 8) {
|
|
141
|
+
this.client.emit("debug", "SERVER => CLIENT | Malformed packet, invalid payload");
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
let parsedData;
|
|
145
|
+
try {
|
|
146
|
+
parsedData = JSON.parse(data.subarray(8, length + 8).toString());
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
this.client.emit("debug", "SERVER => CLIENT | Malformed packet, invalid payload");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this.client.emit("debug", `SERVER => CLIENT | OPCODE.${IPC_OPERATION[op]} |`, parsedData);
|
|
153
|
+
switch (op) {
|
|
154
|
+
case IPC_OPERATION.FRAME: {
|
|
155
|
+
if (!data)
|
|
156
|
+
break;
|
|
157
|
+
this.emit("message", parsedData);
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
case IPC_OPERATION.CLOSE: {
|
|
161
|
+
this.emit("close", parsedData);
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case IPC_OPERATION.PING: {
|
|
165
|
+
this.send(IPC_OPERATION.PONG, parsedData);
|
|
166
|
+
this.emit("ping");
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
this.on("close", (reason) => {
|
|
172
|
+
this.socket = undefined;
|
|
173
|
+
this.client.emit("close", reason);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
send(operation = IPC_OPERATION.FRAME, payload) {
|
|
177
|
+
this.client.emit("debug", `CLIENT => SERVER | OPCODE.${IPC_OPERATION[operation]} |`, payload);
|
|
178
|
+
const dataBuffer = payload
|
|
179
|
+
? Buffer.from(JSON.stringify(payload))
|
|
180
|
+
: Buffer.alloc(0);
|
|
181
|
+
const packet = Buffer.alloc(8);
|
|
182
|
+
packet.writeUInt32LE(operation, 0);
|
|
183
|
+
packet.writeUInt32LE(dataBuffer.length, 4);
|
|
184
|
+
this.socket?.write(Buffer.concat([packet, dataBuffer]));
|
|
185
|
+
}
|
|
186
|
+
ping() {
|
|
187
|
+
this.send(IPC_OPERATION.PING, crypto.randomUUID());
|
|
188
|
+
}
|
|
189
|
+
close() {
|
|
190
|
+
if (!this.socket)
|
|
191
|
+
return Promise.resolve();
|
|
192
|
+
return new Promise((resolve) => {
|
|
193
|
+
if (!this.socket)
|
|
194
|
+
return resolve();
|
|
195
|
+
this.socket.once("close", () => {
|
|
196
|
+
this.emit("close", { code: -1, message: "Closed by client" });
|
|
197
|
+
this.socket = undefined;
|
|
198
|
+
resolve();
|
|
199
|
+
});
|
|
200
|
+
this.socket.end();
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
exports.IpcTransport = IpcTransport;
|