@hieuxyz/rpc 1.0.7 → 1.0.9
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/README.md +24 -19
- package/dist/hieuxyz/Client.d.ts +3 -3
- package/dist/hieuxyz/Client.js +4 -4
- package/dist/hieuxyz/gateway/DiscordWebSocket.d.ts +7 -3
- package/dist/hieuxyz/gateway/DiscordWebSocket.js +66 -33
- package/dist/hieuxyz/rpc/HieuxyzRPC.d.ts +7 -0
- package/dist/hieuxyz/rpc/HieuxyzRPC.js +22 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ DISCORD_USER_TOKEN="YOUR_DISCORD_USER_TOKEN_HERE"
|
|
|
43
43
|
|
|
44
44
|
```typescript
|
|
45
45
|
import * as path from 'path';
|
|
46
|
-
import { Client,
|
|
46
|
+
import { Client, LocalImage, logger } from '../src';
|
|
47
47
|
|
|
48
48
|
async function start() {
|
|
49
49
|
const token = process.env.DISCORD_USER_TOKEN;
|
|
@@ -54,7 +54,10 @@ async function start() {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// Initialize client with token
|
|
57
|
-
const client = new Client({
|
|
57
|
+
const client = new Client({
|
|
58
|
+
token,
|
|
59
|
+
alwaysReconnect: true,
|
|
60
|
+
});
|
|
58
61
|
|
|
59
62
|
await client.run();
|
|
60
63
|
|
|
@@ -66,32 +69,30 @@ async function start() {
|
|
|
66
69
|
.setType(0) // 0: Playing
|
|
67
70
|
.setTimestamps(Date.now())
|
|
68
71
|
.setParty(1, 5)
|
|
69
|
-
.setLargeImage(
|
|
70
|
-
.setSmallImage(new LocalImage(path.join(__dirname, 'vscode.png')), "VS Code")
|
|
71
|
-
.setButtons([
|
|
72
|
-
{ label: "View on GitHub", url: "https://github.com/hieuxyz00/hieuxyz_rpc" }
|
|
73
|
-
]);
|
|
72
|
+
.setLargeImage("https://i.ibb.co/MDP0hfTM/typescript.png", "TypeScript")
|
|
73
|
+
.setSmallImage(new LocalImage(path.join(__dirname, 'vscode.png')), "VS Code");
|
|
74
74
|
|
|
75
75
|
await client.rpc.build();
|
|
76
|
+
logger.info("Initial RPC has been set!");
|
|
76
77
|
|
|
77
|
-
setTimeout(
|
|
78
|
-
logger.info("
|
|
78
|
+
setTimeout(() => {
|
|
79
|
+
logger.info("Clearing RPC and resetting builder...");
|
|
80
|
+
client.rpc.clear();
|
|
79
81
|
|
|
80
|
-
// Change the necessary information
|
|
81
82
|
client.rpc
|
|
82
|
-
.
|
|
83
|
-
.
|
|
84
|
-
.
|
|
85
|
-
|
|
86
|
-
await client.rpc.updateRPC();
|
|
83
|
+
.setName("On a break")
|
|
84
|
+
.setDetails("Thinking about the next feature")
|
|
85
|
+
.setLargeImage("mp:external/dZwPAoMNVxT5qYqecH3Mfgxv1RQEdtGBU8nAspOcAo4/https/c.tenor.com/fvuYGhI1vgUAAAAC/tenor.gif", "Coffee Time");
|
|
87
86
|
|
|
88
|
-
|
|
87
|
+
client.rpc.build();
|
|
88
|
+
logger.info("A new RPC has been set after clearing.");
|
|
89
89
|
|
|
90
|
-
},
|
|
90
|
+
}, 20000);
|
|
91
91
|
|
|
92
92
|
process.on('SIGINT', () => {
|
|
93
93
|
logger.info("SIGINT received. Closing connection...");
|
|
94
|
-
client.
|
|
94
|
+
client.rpc.clear();
|
|
95
|
+
client.close(true);
|
|
95
96
|
process.exit(0);
|
|
96
97
|
});
|
|
97
98
|
}
|
|
@@ -139,8 +140,11 @@ This is the main starting point.
|
|
|
139
140
|
- `new Client(options)`: Create a new instance.
|
|
140
141
|
- `options.token` (required): Your Discord user token.
|
|
141
142
|
- `options.apiBaseUrl` (optional): Override the default image proxy service URL.
|
|
143
|
+
- `options.alwaysReconnect` (optional): If `true`, the client will attempt to reconnect even after a normal close (e.g., from `client.close()` or a Discord-initiated close). Defaults to `false`.
|
|
142
144
|
- `client.run()`: Start connecting to Discord Gateway.
|
|
143
145
|
- `client.rpc`: Access the instance of `HieuxyzRPC` to build the state.
|
|
146
|
+
- `client.close(force?: boolean)`: Closes the connection to the Discord Gateway.
|
|
147
|
+
- `force` (optional, boolean): If set to `true`, the client will close permanently and will not attempt to reconnect, overriding the `alwaysReconnect` option. Defaults to `false`.
|
|
144
148
|
|
|
145
149
|
### Class `HieuxyzRPC`
|
|
146
150
|
|
|
@@ -156,7 +160,8 @@ Main builder class for RPC.
|
|
|
156
160
|
- `.setButtons(buttons[])`: Set buttons (up to 2).
|
|
157
161
|
- `.setPlatform(platform)`: Lay the platform (`'desktop'`, `'xbox'`, `'ps5'`).
|
|
158
162
|
- `.build()`: First RPC send.
|
|
159
|
-
- `.updateRPC()`: Send updates to an existing RPC.(it just call build() lol)
|
|
163
|
+
- `.updateRPC()`: Send updates to an existing RPC.(it just call build() lol).
|
|
164
|
+
- `.clear()`: Clears the current Rich Presence from the user's profile and resets the builder to its default state.
|
|
160
165
|
|
|
161
166
|
### Types of images
|
|
162
167
|
|
package/dist/hieuxyz/Client.d.ts
CHANGED
|
@@ -48,8 +48,8 @@ export declare class Client {
|
|
|
48
48
|
run(): Promise<void>;
|
|
49
49
|
/**
|
|
50
50
|
* Close the connection to Discord Gateway.
|
|
51
|
-
*
|
|
52
|
-
*
|
|
51
|
+
* @param {boolean} [force=false] - If true, the client will close permanently and will not attempt to reconnect,
|
|
52
|
+
* even if `alwaysReconnect` is enabled. Defaults to false.
|
|
53
53
|
*/
|
|
54
|
-
close(): void;
|
|
54
|
+
close(force?: boolean): void;
|
|
55
55
|
}
|
package/dist/hieuxyz/Client.js
CHANGED
|
@@ -55,12 +55,12 @@ class Client {
|
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
57
|
* Close the connection to Discord Gateway.
|
|
58
|
-
*
|
|
59
|
-
*
|
|
58
|
+
* @param {boolean} [force=false] - If true, the client will close permanently and will not attempt to reconnect,
|
|
59
|
+
* even if `alwaysReconnect` is enabled. Defaults to false.
|
|
60
60
|
*/
|
|
61
|
-
close() {
|
|
61
|
+
close(force = false) {
|
|
62
62
|
this.rpc.stopBackgroundRenewal();
|
|
63
|
-
this.websocket.close();
|
|
63
|
+
this.websocket.close(force);
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
exports.Client = Client;
|
|
@@ -11,9 +11,12 @@ export declare class DiscordWebSocket {
|
|
|
11
11
|
private ws;
|
|
12
12
|
private sequence;
|
|
13
13
|
private heartbeatInterval;
|
|
14
|
+
private heartbeatIntervalValue;
|
|
14
15
|
private sessionId;
|
|
15
16
|
private resumeGatewayUrl;
|
|
16
17
|
private options;
|
|
18
|
+
private isReconnecting;
|
|
19
|
+
private permanentClose;
|
|
17
20
|
private resolveReady;
|
|
18
21
|
/**
|
|
19
22
|
* A promise will be resolved when the Gateway connection is ready.
|
|
@@ -34,9 +37,9 @@ export declare class DiscordWebSocket {
|
|
|
34
37
|
* If there was a previous session, it will try to resume.
|
|
35
38
|
*/
|
|
36
39
|
connect(): void;
|
|
37
|
-
private reconnect;
|
|
38
40
|
private onMessage;
|
|
39
41
|
private startHeartbeating;
|
|
42
|
+
private sendHeartbeat;
|
|
40
43
|
private identify;
|
|
41
44
|
private resume;
|
|
42
45
|
/**
|
|
@@ -46,9 +49,10 @@ export declare class DiscordWebSocket {
|
|
|
46
49
|
sendActivity(presence: PresenceUpdatePayload): void;
|
|
47
50
|
private sendJson;
|
|
48
51
|
/**
|
|
49
|
-
*
|
|
52
|
+
* Closes the WebSocket connection.
|
|
53
|
+
* @param force If true, prevents any automatic reconnection attempts.
|
|
50
54
|
*/
|
|
51
|
-
close(): void;
|
|
55
|
+
close(force?: boolean): void;
|
|
52
56
|
private cleanupHeartbeat;
|
|
53
57
|
private shouldReconnect;
|
|
54
58
|
}
|
|
@@ -17,9 +17,12 @@ class DiscordWebSocket {
|
|
|
17
17
|
ws = null;
|
|
18
18
|
sequence = null;
|
|
19
19
|
heartbeatInterval = null;
|
|
20
|
+
heartbeatIntervalValue = 0;
|
|
20
21
|
sessionId = null;
|
|
21
22
|
resumeGatewayUrl = null;
|
|
22
23
|
options;
|
|
24
|
+
isReconnecting = false;
|
|
25
|
+
permanentClose = false;
|
|
23
26
|
resolveReady = () => { };
|
|
24
27
|
/**
|
|
25
28
|
* A promise will be resolved when the Gateway connection is ready.
|
|
@@ -51,18 +54,40 @@ class DiscordWebSocket {
|
|
|
51
54
|
* If there was a previous session, it will try to resume.
|
|
52
55
|
*/
|
|
53
56
|
connect() {
|
|
57
|
+
if (this.isReconnecting) {
|
|
58
|
+
logger_1.logger.info("Connection attempt aborted: reconnection already in progress.");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.permanentClose = false;
|
|
62
|
+
this.isReconnecting = true;
|
|
54
63
|
this.resetReadyPromise();
|
|
55
64
|
const url = this.resumeGatewayUrl || "wss://gateway.discord.gg/?v=10&encoding=json";
|
|
56
65
|
logger_1.logger.info(`Attempting to connect to ${url}...`);
|
|
57
66
|
this.ws = new ws_1.default(url);
|
|
58
|
-
this.ws.on('open', () =>
|
|
67
|
+
this.ws.on('open', () => {
|
|
68
|
+
logger_1.logger.info(`Successfully connected to Discord Gateway at ${url}.`);
|
|
69
|
+
this.isReconnecting = false;
|
|
70
|
+
});
|
|
59
71
|
this.ws.on('message', this.onMessage.bind(this));
|
|
60
72
|
this.ws.on('close', (code, reason) => {
|
|
61
|
-
logger_1.logger.warn(`Connection closed: ${code} - ${reason.toString()}`);
|
|
73
|
+
logger_1.logger.warn(`Connection closed: ${code} - ${reason.toString('utf-8')}`);
|
|
62
74
|
this.cleanupHeartbeat();
|
|
75
|
+
if (this.permanentClose) {
|
|
76
|
+
logger_1.logger.info("Connection permanently closed by client. Not reconnecting.");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (this.isReconnecting)
|
|
80
|
+
return;
|
|
63
81
|
if (this.shouldReconnect(code)) {
|
|
64
|
-
|
|
65
|
-
|
|
82
|
+
setTimeout(() => {
|
|
83
|
+
const canResume = code !== 4004 && !!this.sessionId;
|
|
84
|
+
if (!canResume) {
|
|
85
|
+
this.sessionId = null;
|
|
86
|
+
this.sequence = null;
|
|
87
|
+
this.resumeGatewayUrl = null;
|
|
88
|
+
}
|
|
89
|
+
this.connect();
|
|
90
|
+
}, 500);
|
|
66
91
|
}
|
|
67
92
|
else {
|
|
68
93
|
logger_1.logger.info("Not attempting to reconnect based on close code and client options.");
|
|
@@ -72,15 +97,6 @@ class DiscordWebSocket {
|
|
|
72
97
|
logger_1.logger.error(`WebSocket Error: ${err.message}`);
|
|
73
98
|
});
|
|
74
99
|
}
|
|
75
|
-
reconnect(forceNewSession = false) {
|
|
76
|
-
this.ws?.terminate();
|
|
77
|
-
if (forceNewSession) {
|
|
78
|
-
logger_1.logger.warn("Forcing a new session. Clearing previous session data.");
|
|
79
|
-
this.sessionId = null;
|
|
80
|
-
this.resumeGatewayUrl = null;
|
|
81
|
-
}
|
|
82
|
-
setTimeout(() => this.connect(), 3000);
|
|
83
|
-
}
|
|
84
100
|
onMessage(data) {
|
|
85
101
|
const payload = JSON.parse(data.toString());
|
|
86
102
|
if (payload.s) {
|
|
@@ -88,8 +104,10 @@ class DiscordWebSocket {
|
|
|
88
104
|
}
|
|
89
105
|
switch (payload.op) {
|
|
90
106
|
case OpCode_1.OpCode.HELLO:
|
|
91
|
-
this.
|
|
92
|
-
|
|
107
|
+
this.heartbeatIntervalValue = payload.d.heartbeat_interval;
|
|
108
|
+
logger_1.logger.info(`Received HELLO. Setting heartbeat interval to ${this.heartbeatIntervalValue}ms.`);
|
|
109
|
+
this.startHeartbeating();
|
|
110
|
+
if (this.sessionId && this.sequence) {
|
|
93
111
|
this.resume();
|
|
94
112
|
}
|
|
95
113
|
else {
|
|
@@ -99,8 +117,8 @@ class DiscordWebSocket {
|
|
|
99
117
|
case OpCode_1.OpCode.DISPATCH:
|
|
100
118
|
if (payload.t === 'READY') {
|
|
101
119
|
this.sessionId = payload.d.session_id;
|
|
102
|
-
this.resumeGatewayUrl = payload.d.resume_gateway_url;
|
|
103
|
-
logger_1.logger.info(`Session
|
|
120
|
+
this.resumeGatewayUrl = payload.d.resume_gateway_url + "/?v=10&encoding=json";
|
|
121
|
+
logger_1.logger.info(`Session READY. Session ID: ${this.sessionId}. Resume URL set.`);
|
|
104
122
|
this.resolveReady();
|
|
105
123
|
}
|
|
106
124
|
else if (payload.t === 'RESUMED') {
|
|
@@ -112,28 +130,27 @@ class DiscordWebSocket {
|
|
|
112
130
|
logger_1.logger.info("Heartbeat acknowledged.");
|
|
113
131
|
break;
|
|
114
132
|
case OpCode_1.OpCode.INVALID_SESSION:
|
|
115
|
-
logger_1.logger.warn(`
|
|
116
|
-
if (payload.d
|
|
117
|
-
this.ws?.close(
|
|
133
|
+
logger_1.logger.warn(`Received INVALID_SESSION. Resumable: ${payload.d}`);
|
|
134
|
+
if (payload.d) {
|
|
135
|
+
this.ws?.close(4000, "Invalid session, attempting to resume.");
|
|
118
136
|
}
|
|
119
137
|
else {
|
|
120
|
-
this.ws?.close(
|
|
138
|
+
this.ws?.close(4004, "Invalid session, starting a new session.");
|
|
121
139
|
}
|
|
122
140
|
break;
|
|
123
141
|
case OpCode_1.OpCode.RECONNECT:
|
|
124
|
-
logger_1.logger.info("Gateway requested
|
|
125
|
-
this.ws?.close(4000, "
|
|
142
|
+
logger_1.logger.info("Gateway requested RECONNECT. Closing to reconnect and resume.");
|
|
143
|
+
this.ws?.close(4000, "Gateway requested reconnect.");
|
|
126
144
|
break;
|
|
127
145
|
default:
|
|
128
146
|
break;
|
|
129
147
|
}
|
|
130
148
|
}
|
|
131
|
-
startHeartbeating(
|
|
149
|
+
startHeartbeating() {
|
|
132
150
|
this.cleanupHeartbeat();
|
|
133
151
|
setTimeout(() => {
|
|
134
152
|
if (this.ws?.readyState === ws_1.default.OPEN) {
|
|
135
|
-
this.
|
|
136
|
-
logger_1.logger.info(`Initial heartbeat sent with sequence ${this.sequence}.`);
|
|
153
|
+
this.sendHeartbeat();
|
|
137
154
|
}
|
|
138
155
|
this.heartbeatInterval = setInterval(() => {
|
|
139
156
|
if (this.ws?.readyState !== ws_1.default.OPEN) {
|
|
@@ -141,10 +158,15 @@ class DiscordWebSocket {
|
|
|
141
158
|
this.cleanupHeartbeat();
|
|
142
159
|
return;
|
|
143
160
|
}
|
|
144
|
-
this.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
161
|
+
this.sendHeartbeat();
|
|
162
|
+
}, this.heartbeatIntervalValue);
|
|
163
|
+
}, this.heartbeatIntervalValue * Math.random());
|
|
164
|
+
}
|
|
165
|
+
sendHeartbeat() {
|
|
166
|
+
if (this.ws?.readyState !== ws_1.default.OPEN)
|
|
167
|
+
return;
|
|
168
|
+
this.sendJson({ op: OpCode_1.OpCode.HEARTBEAT, d: this.sequence });
|
|
169
|
+
logger_1.logger.info(`Heartbeat sent with sequence ${this.sequence}.`);
|
|
148
170
|
}
|
|
149
171
|
identify() {
|
|
150
172
|
const identifyPayload = (0, identify_1.getIdentifyPayload)(this.token);
|
|
@@ -182,10 +204,21 @@ class DiscordWebSocket {
|
|
|
182
204
|
}
|
|
183
205
|
}
|
|
184
206
|
/**
|
|
185
|
-
*
|
|
207
|
+
* Closes the WebSocket connection.
|
|
208
|
+
* @param force If true, prevents any automatic reconnection attempts.
|
|
186
209
|
*/
|
|
187
|
-
close() {
|
|
188
|
-
|
|
210
|
+
close(force = false) {
|
|
211
|
+
if (force) {
|
|
212
|
+
logger_1.logger.info("Forcing permanent closure. Reconnects will be disabled.");
|
|
213
|
+
this.permanentClose = true;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
logger_1.logger.info("Closing connection manually...");
|
|
217
|
+
}
|
|
218
|
+
this.isReconnecting = false;
|
|
219
|
+
if (this.ws) {
|
|
220
|
+
this.ws.close(1000, "Client initiated closure");
|
|
221
|
+
}
|
|
189
222
|
}
|
|
190
223
|
cleanupHeartbeat() {
|
|
191
224
|
if (this.heartbeatInterval) {
|
|
@@ -125,5 +125,12 @@ export declare class HieuxyzRPC {
|
|
|
125
125
|
* @returns {Promise<void>}
|
|
126
126
|
*/
|
|
127
127
|
updateRPC(): Promise<void>;
|
|
128
|
+
/**
|
|
129
|
+
* Clears the current Rich Presence from Discord and resets the builder state.
|
|
130
|
+
* This sends an empty activity payload to Discord and then resets all configured
|
|
131
|
+
* options (name, details, images, etc.) to their default values, allowing you
|
|
132
|
+
* to build a new presence from scratch.
|
|
133
|
+
*/
|
|
134
|
+
clear(): void;
|
|
128
135
|
}
|
|
129
136
|
export {};
|
|
@@ -288,7 +288,7 @@ class HieuxyzRPC {
|
|
|
288
288
|
since: 0,
|
|
289
289
|
activities: [activity],
|
|
290
290
|
status: this.status,
|
|
291
|
-
afk:
|
|
291
|
+
afk: true,
|
|
292
292
|
};
|
|
293
293
|
this.websocket.sendActivity(presencePayload);
|
|
294
294
|
}
|
|
@@ -300,5 +300,26 @@ class HieuxyzRPC {
|
|
|
300
300
|
async updateRPC() {
|
|
301
301
|
await this.build();
|
|
302
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* Clears the current Rich Presence from Discord and resets the builder state.
|
|
305
|
+
* This sends an empty activity payload to Discord and then resets all configured
|
|
306
|
+
* options (name, details, images, etc.) to their default values, allowing you
|
|
307
|
+
* to build a new presence from scratch.
|
|
308
|
+
*/
|
|
309
|
+
clear() {
|
|
310
|
+
const clearPayload = {
|
|
311
|
+
since: 0,
|
|
312
|
+
activities: [],
|
|
313
|
+
status: this.status,
|
|
314
|
+
afk: true,
|
|
315
|
+
};
|
|
316
|
+
this.websocket.sendActivity(clearPayload);
|
|
317
|
+
logger_1.logger.info("Rich Presence cleared from Discord.");
|
|
318
|
+
this.activity = {};
|
|
319
|
+
this.assets = {};
|
|
320
|
+
this.applicationId = '1416676323459469363'; // Reset to default
|
|
321
|
+
this.platform = 'desktop'; // Reset to default
|
|
322
|
+
logger_1.logger.info("RPC builder has been reset to its initial state.");
|
|
323
|
+
}
|
|
303
324
|
}
|
|
304
325
|
exports.HieuxyzRPC = HieuxyzRPC;
|