@hieuxyz/rpc 1.0.9 → 1.0.91
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 +29 -13
- package/dist/hieuxyz/Client.js +3 -3
- package/dist/hieuxyz/gateway/DiscordWebSocket.js +26 -25
- package/dist/hieuxyz/gateway/entities/identify.d.ts +1 -1
- package/dist/hieuxyz/gateway/entities/identify.js +3 -3
- package/dist/hieuxyz/gateway/entities/types.d.ts +2 -1
- package/dist/hieuxyz/rpc/HieuxyzRPC.d.ts +19 -4
- package/dist/hieuxyz/rpc/HieuxyzRPC.js +61 -14
- package/dist/hieuxyz/rpc/ImageService.js +2 -2
- package/dist/hieuxyz/rpc/RpcImage.d.ts +1 -1
- package/dist/hieuxyz/rpc/RpcImage.js +1 -1
- package/package.json +12 -4
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, LocalImage, logger } from '
|
|
46
|
+
import { Client, LocalImage, logger } from '@hieuxyz/rpc';
|
|
47
47
|
|
|
48
48
|
async function start() {
|
|
49
49
|
const token = process.env.DISCORD_USER_TOKEN;
|
|
@@ -150,18 +150,34 @@ This is the main starting point.
|
|
|
150
150
|
|
|
151
151
|
Main builder class for RPC.
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
- `.
|
|
155
|
-
- `.
|
|
156
|
-
- `.
|
|
157
|
-
- `.
|
|
158
|
-
- `.
|
|
159
|
-
- `.
|
|
160
|
-
- `.
|
|
161
|
-
- `.
|
|
162
|
-
- `.
|
|
163
|
-
- `.
|
|
164
|
-
- `.
|
|
153
|
+
#### Setter Methods
|
|
154
|
+
- `.setName(string)`: Sets the activity name (first line).
|
|
155
|
+
- `.setDetails(string)`: Sets the activity details (second line).
|
|
156
|
+
- `.setState(string)`: Sets the activity state (third line).
|
|
157
|
+
- `.setTimestamps(start?, end?)`: Sets the start and/or end times.
|
|
158
|
+
- `.setParty(current, max)`: Sets the party information.
|
|
159
|
+
- `.setLargeImage(RpcImage, text?)`: Sets the large image and its tooltip text.
|
|
160
|
+
- `.setSmallImage(RpcImage, text?)`: Sets the small image and its tooltip text.
|
|
161
|
+
- `.setButtons(buttons[])`: Sets up to two clickable buttons.
|
|
162
|
+
- `.setPlatform(platform)`: Sets the platform (`'desktop'`, `'xbox'`, etc.).
|
|
163
|
+
- `.setInstance(boolean)`: Marks the activity as a specific, joinable instance.
|
|
164
|
+
- `.setApplicationId(string)`: Sets a custom Application ID.
|
|
165
|
+
- `.setStatus('online' | ...)`: Sets the user's presence status.
|
|
166
|
+
|
|
167
|
+
#### Clearer Methods
|
|
168
|
+
- `.clearDetails()`: Removes the activity details.
|
|
169
|
+
- `.clearState()`: Removes the activity state.
|
|
170
|
+
- `.clearTimestamps()`: Removes the timestamps.
|
|
171
|
+
- `.clearParty()`: Removes the party information.
|
|
172
|
+
- `.clearLargeImage()`: Removes the large image and its text.
|
|
173
|
+
- `.clearSmallImage()`: Removes the small image and its text.
|
|
174
|
+
- `.clearButtons()`: Removes all buttons.
|
|
175
|
+
- `.clearInstance()`: Removes the instance flag.
|
|
176
|
+
|
|
177
|
+
#### Core Methods
|
|
178
|
+
- `.build()`: Builds and sends the presence payload to Discord.
|
|
179
|
+
- `.updateRPC()`: Builds and sends an updated presence payload. (Alias for `build()`).
|
|
180
|
+
- `.clear()`: Clears the entire Rich Presence from the user's profile and resets the builder to its default state.
|
|
165
181
|
|
|
166
182
|
### Types of images
|
|
167
183
|
|
package/dist/hieuxyz/Client.js
CHANGED
|
@@ -33,7 +33,7 @@ class Client {
|
|
|
33
33
|
*/
|
|
34
34
|
constructor(options) {
|
|
35
35
|
if (!options.token) {
|
|
36
|
-
throw new Error(
|
|
36
|
+
throw new Error('Tokens are required to connect to Discord.');
|
|
37
37
|
}
|
|
38
38
|
this.token = options.token;
|
|
39
39
|
this.imageService = new ImageService_1.ImageService(options.apiBaseUrl);
|
|
@@ -49,9 +49,9 @@ class Client {
|
|
|
49
49
|
*/
|
|
50
50
|
async run() {
|
|
51
51
|
this.websocket.connect();
|
|
52
|
-
logger_1.logger.info(
|
|
52
|
+
logger_1.logger.info('Waiting for Discord session to be ready...');
|
|
53
53
|
await this.websocket.readyPromise;
|
|
54
|
-
logger_1.logger.info(
|
|
54
|
+
logger_1.logger.info('Client is ready to send Rich Presence updates.');
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
57
|
* Close the connection to Discord Gateway.
|
|
@@ -37,17 +37,18 @@ class DiscordWebSocket {
|
|
|
37
37
|
*/
|
|
38
38
|
constructor(token, options) {
|
|
39
39
|
if (!this.isTokenValid(token)) {
|
|
40
|
-
throw new Error(
|
|
40
|
+
throw new Error('Invalid token provided.');
|
|
41
41
|
}
|
|
42
42
|
this.token = token;
|
|
43
43
|
this.options = options;
|
|
44
|
-
this.readyPromise = new Promise(resolve => (this.resolveReady = resolve));
|
|
44
|
+
this.readyPromise = new Promise((resolve) => (this.resolveReady = resolve));
|
|
45
45
|
}
|
|
46
46
|
resetReadyPromise() {
|
|
47
|
-
this.readyPromise = new Promise(resolve => (this.resolveReady = resolve));
|
|
47
|
+
this.readyPromise = new Promise((resolve) => (this.resolveReady = resolve));
|
|
48
48
|
}
|
|
49
49
|
isTokenValid(token) {
|
|
50
|
-
return /^[a-zA-Z0-9_-]{24}\.[a-zA-Z0-9_-]{6}\.[a-zA-Z0-9_-]{38}$/.test(token) ||
|
|
50
|
+
return (/^[a-zA-Z0-9_-]{24}\.[a-zA-Z0-9_-]{6}\.[a-zA-Z0-9_-]{38}$/.test(token) ||
|
|
51
|
+
/^mfa\.[a-zA-Z0-9_-]{84}$/.test(token));
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
53
54
|
* Initiate connection to Discord Gateway.
|
|
@@ -55,13 +56,13 @@ class DiscordWebSocket {
|
|
|
55
56
|
*/
|
|
56
57
|
connect() {
|
|
57
58
|
if (this.isReconnecting) {
|
|
58
|
-
logger_1.logger.info(
|
|
59
|
+
logger_1.logger.info('Connection attempt aborted: reconnection already in progress.');
|
|
59
60
|
return;
|
|
60
61
|
}
|
|
61
62
|
this.permanentClose = false;
|
|
62
63
|
this.isReconnecting = true;
|
|
63
64
|
this.resetReadyPromise();
|
|
64
|
-
const url = this.resumeGatewayUrl ||
|
|
65
|
+
const url = this.resumeGatewayUrl || 'wss://gateway.discord.gg/?v=10&encoding=json';
|
|
65
66
|
logger_1.logger.info(`Attempting to connect to ${url}...`);
|
|
66
67
|
this.ws = new ws_1.default(url);
|
|
67
68
|
this.ws.on('open', () => {
|
|
@@ -73,7 +74,7 @@ class DiscordWebSocket {
|
|
|
73
74
|
logger_1.logger.warn(`Connection closed: ${code} - ${reason.toString('utf-8')}`);
|
|
74
75
|
this.cleanupHeartbeat();
|
|
75
76
|
if (this.permanentClose) {
|
|
76
|
-
logger_1.logger.info(
|
|
77
|
+
logger_1.logger.info('Connection permanently closed by client. Not reconnecting.');
|
|
77
78
|
return;
|
|
78
79
|
}
|
|
79
80
|
if (this.isReconnecting)
|
|
@@ -90,7 +91,7 @@ class DiscordWebSocket {
|
|
|
90
91
|
}, 500);
|
|
91
92
|
}
|
|
92
93
|
else {
|
|
93
|
-
logger_1.logger.info(
|
|
94
|
+
logger_1.logger.info('Not attempting to reconnect based on close code and client options.');
|
|
94
95
|
}
|
|
95
96
|
});
|
|
96
97
|
this.ws.on('error', (err) => {
|
|
@@ -117,30 +118,30 @@ class DiscordWebSocket {
|
|
|
117
118
|
case OpCode_1.OpCode.DISPATCH:
|
|
118
119
|
if (payload.t === 'READY') {
|
|
119
120
|
this.sessionId = payload.d.session_id;
|
|
120
|
-
this.resumeGatewayUrl = payload.d.resume_gateway_url +
|
|
121
|
+
this.resumeGatewayUrl = payload.d.resume_gateway_url + '/?v=10&encoding=json';
|
|
121
122
|
logger_1.logger.info(`Session READY. Session ID: ${this.sessionId}. Resume URL set.`);
|
|
122
123
|
this.resolveReady();
|
|
123
124
|
}
|
|
124
125
|
else if (payload.t === 'RESUMED') {
|
|
125
|
-
logger_1.logger.info(
|
|
126
|
+
logger_1.logger.info('The session has been successfully resumed.');
|
|
126
127
|
this.resolveReady();
|
|
127
128
|
}
|
|
128
129
|
break;
|
|
129
130
|
case OpCode_1.OpCode.HEARTBEAT_ACK:
|
|
130
|
-
logger_1.logger.info(
|
|
131
|
+
logger_1.logger.info('Heartbeat acknowledged.');
|
|
131
132
|
break;
|
|
132
133
|
case OpCode_1.OpCode.INVALID_SESSION:
|
|
133
134
|
logger_1.logger.warn(`Received INVALID_SESSION. Resumable: ${payload.d}`);
|
|
134
135
|
if (payload.d) {
|
|
135
|
-
this.ws?.close(4000,
|
|
136
|
+
this.ws?.close(4000, 'Invalid session, attempting to resume.');
|
|
136
137
|
}
|
|
137
138
|
else {
|
|
138
|
-
this.ws?.close(4004,
|
|
139
|
+
this.ws?.close(4004, 'Invalid session, starting a new session.');
|
|
139
140
|
}
|
|
140
141
|
break;
|
|
141
142
|
case OpCode_1.OpCode.RECONNECT:
|
|
142
|
-
logger_1.logger.info(
|
|
143
|
-
this.ws?.close(4000,
|
|
143
|
+
logger_1.logger.info('Gateway requested RECONNECT. Closing to reconnect and resume.');
|
|
144
|
+
this.ws?.close(4000, 'Gateway requested reconnect.');
|
|
144
145
|
break;
|
|
145
146
|
default:
|
|
146
147
|
break;
|
|
@@ -154,7 +155,7 @@ class DiscordWebSocket {
|
|
|
154
155
|
}
|
|
155
156
|
this.heartbeatInterval = setInterval(() => {
|
|
156
157
|
if (this.ws?.readyState !== ws_1.default.OPEN) {
|
|
157
|
-
logger_1.logger.warn(
|
|
158
|
+
logger_1.logger.warn('Heartbeat skipped: WebSocket is not open.');
|
|
158
159
|
this.cleanupHeartbeat();
|
|
159
160
|
return;
|
|
160
161
|
}
|
|
@@ -171,21 +172,21 @@ class DiscordWebSocket {
|
|
|
171
172
|
identify() {
|
|
172
173
|
const identifyPayload = (0, identify_1.getIdentifyPayload)(this.token);
|
|
173
174
|
this.sendJson({ op: OpCode_1.OpCode.IDENTIFY, d: identifyPayload });
|
|
174
|
-
logger_1.logger.info(
|
|
175
|
+
logger_1.logger.info('Identify payload sent.');
|
|
175
176
|
}
|
|
176
177
|
resume() {
|
|
177
178
|
if (!this.sessionId || this.sequence === null) {
|
|
178
|
-
logger_1.logger.error(
|
|
179
|
+
logger_1.logger.error('Attempted to resume without session ID or sequence. Falling back to identify.');
|
|
179
180
|
this.identify();
|
|
180
181
|
return;
|
|
181
182
|
}
|
|
182
183
|
const resumePayload = {
|
|
183
184
|
token: this.token,
|
|
184
185
|
session_id: this.sessionId,
|
|
185
|
-
seq: this.sequence
|
|
186
|
+
seq: this.sequence,
|
|
186
187
|
};
|
|
187
188
|
this.sendJson({ op: OpCode_1.OpCode.RESUME, d: resumePayload });
|
|
188
|
-
logger_1.logger.info(
|
|
189
|
+
logger_1.logger.info('Resume payload sent.');
|
|
189
190
|
}
|
|
190
191
|
/**
|
|
191
192
|
* Send presence update payload to Gateway.
|
|
@@ -193,14 +194,14 @@ class DiscordWebSocket {
|
|
|
193
194
|
*/
|
|
194
195
|
sendActivity(presence) {
|
|
195
196
|
this.sendJson({ op: OpCode_1.OpCode.PRESENCE_UPDATE, d: presence });
|
|
196
|
-
logger_1.logger.info(
|
|
197
|
+
logger_1.logger.info('Presence update sent.');
|
|
197
198
|
}
|
|
198
199
|
sendJson(data) {
|
|
199
200
|
if (this.ws?.readyState === ws_1.default.OPEN) {
|
|
200
201
|
this.ws.send(JSON.stringify(data));
|
|
201
202
|
}
|
|
202
203
|
else {
|
|
203
|
-
logger_1.logger.warn(
|
|
204
|
+
logger_1.logger.warn('Attempted to send data while WebSocket was not open.');
|
|
204
205
|
}
|
|
205
206
|
}
|
|
206
207
|
/**
|
|
@@ -209,15 +210,15 @@ class DiscordWebSocket {
|
|
|
209
210
|
*/
|
|
210
211
|
close(force = false) {
|
|
211
212
|
if (force) {
|
|
212
|
-
logger_1.logger.info(
|
|
213
|
+
logger_1.logger.info('Forcing permanent closure. Reconnects will be disabled.');
|
|
213
214
|
this.permanentClose = true;
|
|
214
215
|
}
|
|
215
216
|
else {
|
|
216
|
-
logger_1.logger.info(
|
|
217
|
+
logger_1.logger.info('Closing connection manually...');
|
|
217
218
|
}
|
|
218
219
|
this.isReconnecting = false;
|
|
219
220
|
if (this.ws) {
|
|
220
|
-
this.ws.close(1000,
|
|
221
|
+
this.ws.close(1000, 'Client initiated closure');
|
|
221
222
|
}
|
|
222
223
|
}
|
|
223
224
|
cleanupHeartbeat() {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { IdentifyPayload } from
|
|
1
|
+
import { IdentifyPayload } from './types';
|
|
2
2
|
export declare function getIdentifyPayload(token: string): IdentifyPayload;
|
|
@@ -6,9 +6,9 @@ function getIdentifyPayload(token) {
|
|
|
6
6
|
token: token,
|
|
7
7
|
capabilities: 65,
|
|
8
8
|
properties: {
|
|
9
|
-
os:
|
|
10
|
-
browser:
|
|
11
|
-
device:
|
|
9
|
+
os: 'Windows',
|
|
10
|
+
browser: 'Discord Client',
|
|
11
|
+
device: 'hieuxyz©rpc',
|
|
12
12
|
},
|
|
13
13
|
compress: false,
|
|
14
14
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OpCode } from
|
|
1
|
+
import { OpCode } from './OpCode';
|
|
2
2
|
export declare enum ActivityType {
|
|
3
3
|
Playing = 0,
|
|
4
4
|
Streaming = 1,
|
|
@@ -32,6 +32,7 @@ export interface Activity {
|
|
|
32
32
|
details?: string;
|
|
33
33
|
state?: string;
|
|
34
34
|
platform?: string;
|
|
35
|
+
instance?: boolean;
|
|
35
36
|
party?: {
|
|
36
37
|
id?: string;
|
|
37
38
|
size?: [number, number];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { DiscordWebSocket } from
|
|
2
|
-
import { SettableActivityType } from
|
|
3
|
-
import { ImageService } from
|
|
4
|
-
import { RpcImage } from
|
|
1
|
+
import { DiscordWebSocket } from '../gateway/DiscordWebSocket';
|
|
2
|
+
import { SettableActivityType } from '../gateway/entities/types';
|
|
3
|
+
import { ImageService } from './ImageService';
|
|
4
|
+
import { RpcImage } from './RpcImage';
|
|
5
5
|
interface RpcButton {
|
|
6
6
|
label: string;
|
|
7
7
|
url: string;
|
|
@@ -27,6 +27,7 @@ export declare class HieuxyzRPC {
|
|
|
27
27
|
private renewalInterval;
|
|
28
28
|
constructor(websocket: DiscordWebSocket, imageService: ImageService);
|
|
29
29
|
private _toRpcImage;
|
|
30
|
+
private cleanupNulls;
|
|
30
31
|
private sanitize;
|
|
31
32
|
/**
|
|
32
33
|
* Name the operation (first line of RPC).
|
|
@@ -105,6 +106,20 @@ export declare class HieuxyzRPC {
|
|
|
105
106
|
* @returns {this}
|
|
106
107
|
*/
|
|
107
108
|
setPlatform(platform: DiscordPlatform): this;
|
|
109
|
+
/**
|
|
110
|
+
* Marks the activity as a joinable instance for the game.
|
|
111
|
+
* @param instance - Whether this activity is a specific instance.
|
|
112
|
+
* @returns {this}
|
|
113
|
+
*/
|
|
114
|
+
setInstance(instance: boolean): this;
|
|
115
|
+
clearDetails(): this;
|
|
116
|
+
clearState(): this;
|
|
117
|
+
clearTimestamps(): this;
|
|
118
|
+
clearParty(): this;
|
|
119
|
+
clearButtons(): this;
|
|
120
|
+
clearInstance(): this;
|
|
121
|
+
clearLargeImage(): this;
|
|
122
|
+
clearSmallImage(): this;
|
|
108
123
|
private getExpiryTime;
|
|
109
124
|
private renewAssetIfNeeded;
|
|
110
125
|
private startBackgroundRenewal;
|
|
@@ -42,7 +42,7 @@ class HieuxyzRPC {
|
|
|
42
42
|
return new RpcImage_1.ExternalImage(source);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
catch
|
|
45
|
+
catch {
|
|
46
46
|
logger_1.logger.warn(`Could not parse "${source}" into a valid URL. Treating as RawImage.`);
|
|
47
47
|
return new RpcImage_1.RawImage(source);
|
|
48
48
|
}
|
|
@@ -52,6 +52,9 @@ class HieuxyzRPC {
|
|
|
52
52
|
}
|
|
53
53
|
return new RpcImage_1.RawImage(source);
|
|
54
54
|
}
|
|
55
|
+
cleanupNulls(obj) {
|
|
56
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== null && v !== undefined));
|
|
57
|
+
}
|
|
55
58
|
sanitize(str, length = 128) {
|
|
56
59
|
return str.length > length ? str.substring(0, length) : str;
|
|
57
60
|
}
|
|
@@ -155,8 +158,8 @@ class HieuxyzRPC {
|
|
|
155
158
|
*/
|
|
156
159
|
setButtons(buttons) {
|
|
157
160
|
const validButtons = buttons.slice(0, 2);
|
|
158
|
-
this.activity.buttons = validButtons.map(b => this.sanitize(b.label, 32));
|
|
159
|
-
this.activity.metadata = { button_urls: validButtons.map(b => b.url) };
|
|
161
|
+
this.activity.buttons = validButtons.map((b) => this.sanitize(b.label, 32));
|
|
162
|
+
this.activity.metadata = { button_urls: validButtons.map((b) => b.url) };
|
|
160
163
|
return this;
|
|
161
164
|
}
|
|
162
165
|
/**
|
|
@@ -167,7 +170,7 @@ class HieuxyzRPC {
|
|
|
167
170
|
*/
|
|
168
171
|
setApplicationId(id) {
|
|
169
172
|
if (!/^\d{18,19}$/.test(id)) {
|
|
170
|
-
throw new Error(
|
|
173
|
+
throw new Error('The app ID must be an 18 or 19 digit number.');
|
|
171
174
|
}
|
|
172
175
|
this.applicationId = id;
|
|
173
176
|
return this;
|
|
@@ -190,6 +193,50 @@ class HieuxyzRPC {
|
|
|
190
193
|
this.platform = platform;
|
|
191
194
|
return this;
|
|
192
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Marks the activity as a joinable instance for the game.
|
|
198
|
+
* @param instance - Whether this activity is a specific instance.
|
|
199
|
+
* @returns {this}
|
|
200
|
+
*/
|
|
201
|
+
setInstance(instance) {
|
|
202
|
+
this.activity.instance = instance;
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
clearDetails() {
|
|
206
|
+
this.activity.details = undefined;
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
clearState() {
|
|
210
|
+
this.activity.state = undefined;
|
|
211
|
+
return this;
|
|
212
|
+
}
|
|
213
|
+
clearTimestamps() {
|
|
214
|
+
this.activity.timestamps = undefined;
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
clearParty() {
|
|
218
|
+
this.activity.party = undefined;
|
|
219
|
+
return this;
|
|
220
|
+
}
|
|
221
|
+
clearButtons() {
|
|
222
|
+
this.activity.buttons = undefined;
|
|
223
|
+
this.activity.metadata = undefined;
|
|
224
|
+
return this;
|
|
225
|
+
}
|
|
226
|
+
clearInstance() {
|
|
227
|
+
this.activity.instance = undefined;
|
|
228
|
+
return this;
|
|
229
|
+
}
|
|
230
|
+
clearLargeImage() {
|
|
231
|
+
this.assets.large_image = undefined;
|
|
232
|
+
this.assets.large_text = undefined;
|
|
233
|
+
return this;
|
|
234
|
+
}
|
|
235
|
+
clearSmallImage() {
|
|
236
|
+
this.assets.small_image = undefined;
|
|
237
|
+
this.assets.small_text = undefined;
|
|
238
|
+
return this;
|
|
239
|
+
}
|
|
193
240
|
getExpiryTime(assetKey) {
|
|
194
241
|
if (!assetKey.startsWith('mp:attachments'))
|
|
195
242
|
return null;
|
|
@@ -201,14 +248,14 @@ class HieuxyzRPC {
|
|
|
201
248
|
return parseInt(expiresTimestamp, 16) * 1000;
|
|
202
249
|
}
|
|
203
250
|
}
|
|
204
|
-
catch
|
|
251
|
+
catch {
|
|
205
252
|
logger_1.logger.error(`Could not parse asset URL for expiry check: ${assetKey}`);
|
|
206
253
|
}
|
|
207
254
|
return null;
|
|
208
255
|
}
|
|
209
256
|
async renewAssetIfNeeded(cacheKey, assetKey) {
|
|
210
257
|
const expiryTimeMs = this.getExpiryTime(assetKey);
|
|
211
|
-
if (expiryTimeMs && expiryTimeMs <
|
|
258
|
+
if (expiryTimeMs && expiryTimeMs < Date.now() + 3600000) {
|
|
212
259
|
logger_1.logger.info(`Asset ${cacheKey} is expiring soon. Renewing...`);
|
|
213
260
|
const assetId = assetKey.split('mp:attachments/')[1];
|
|
214
261
|
const newAsset = await this.imageService.renewImage(assetId);
|
|
@@ -225,7 +272,7 @@ class HieuxyzRPC {
|
|
|
225
272
|
clearInterval(this.renewalInterval);
|
|
226
273
|
}
|
|
227
274
|
this.renewalInterval = setInterval(async () => {
|
|
228
|
-
logger_1.logger.info(
|
|
275
|
+
logger_1.logger.info('Running background asset renewal check...');
|
|
229
276
|
for (const [cacheKey, assetKey] of this.resolvedAssetsCache.entries()) {
|
|
230
277
|
await this.renewAssetIfNeeded(cacheKey, assetKey);
|
|
231
278
|
}
|
|
@@ -238,14 +285,14 @@ class HieuxyzRPC {
|
|
|
238
285
|
if (this.renewalInterval) {
|
|
239
286
|
clearInterval(this.renewalInterval);
|
|
240
287
|
this.renewalInterval = null;
|
|
241
|
-
logger_1.logger.info(
|
|
288
|
+
logger_1.logger.info('Stopped background asset renewal process.');
|
|
242
289
|
}
|
|
243
290
|
}
|
|
244
291
|
async resolveImage(image) {
|
|
245
292
|
if (!image)
|
|
246
293
|
return undefined;
|
|
247
294
|
const cacheKey = image.getCacheKey();
|
|
248
|
-
|
|
295
|
+
const cachedAsset = this.resolvedAssetsCache.get(cacheKey);
|
|
249
296
|
if (cachedAsset) {
|
|
250
297
|
return await this.renewAssetIfNeeded(cacheKey, cachedAsset);
|
|
251
298
|
}
|
|
@@ -267,16 +314,16 @@ class HieuxyzRPC {
|
|
|
267
314
|
if (small_image)
|
|
268
315
|
finalAssets.small_image = small_image;
|
|
269
316
|
const finalActivity = { ...this.activity };
|
|
270
|
-
finalActivity.assets =
|
|
317
|
+
finalActivity.assets = large_image || small_image ? finalAssets : undefined;
|
|
271
318
|
finalActivity.application_id = this.applicationId;
|
|
272
319
|
finalActivity.platform = this.platform;
|
|
273
320
|
if (!finalActivity.name) {
|
|
274
|
-
finalActivity.name =
|
|
321
|
+
finalActivity.name = 'hieuxyzRPC';
|
|
275
322
|
}
|
|
276
323
|
if (typeof finalActivity.type === 'undefined') {
|
|
277
324
|
finalActivity.type = types_1.ActivityType.Playing;
|
|
278
325
|
}
|
|
279
|
-
return finalActivity;
|
|
326
|
+
return this.cleanupNulls(finalActivity);
|
|
280
327
|
}
|
|
281
328
|
/**
|
|
282
329
|
* Build the final Rich Presence payload and send it to Discord.
|
|
@@ -314,12 +361,12 @@ class HieuxyzRPC {
|
|
|
314
361
|
afk: true,
|
|
315
362
|
};
|
|
316
363
|
this.websocket.sendActivity(clearPayload);
|
|
317
|
-
logger_1.logger.info(
|
|
364
|
+
logger_1.logger.info('Rich Presence cleared from Discord.');
|
|
318
365
|
this.activity = {};
|
|
319
366
|
this.assets = {};
|
|
320
367
|
this.applicationId = '1416676323459469363'; // Reset to default
|
|
321
368
|
this.platform = 'desktop'; // Reset to default
|
|
322
|
-
logger_1.logger.info(
|
|
369
|
+
logger_1.logger.info('RPC builder has been reset to its initial state.');
|
|
323
370
|
}
|
|
324
371
|
}
|
|
325
372
|
exports.HieuxyzRPC = HieuxyzRPC;
|
|
@@ -90,8 +90,8 @@ class ImageService {
|
|
|
90
90
|
form.append('file_name', fileName);
|
|
91
91
|
const response = await this.apiClient.post('/upload', form, {
|
|
92
92
|
headers: {
|
|
93
|
-
...form.getHeaders()
|
|
94
|
-
}
|
|
93
|
+
...form.getHeaders(),
|
|
94
|
+
},
|
|
95
95
|
});
|
|
96
96
|
if (response.data && response.data.id) {
|
|
97
97
|
return response.data.id;
|
|
@@ -51,6 +51,6 @@ export declare class LocalImage extends RpcImage {
|
|
|
51
51
|
export declare class RawImage extends RpcImage {
|
|
52
52
|
private assetKey;
|
|
53
53
|
constructor(assetKey: string);
|
|
54
|
-
resolve(
|
|
54
|
+
resolve(__imageService: ImageService): Promise<string | undefined>;
|
|
55
55
|
getCacheKey(): string;
|
|
56
56
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hieuxyz/rpc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.91",
|
|
4
4
|
"description": "A Discord Rich Presence library for Node.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build": "tsc",
|
|
14
|
-
"dev": "node -r ts-node/register --env-file=.env examples/main.ts"
|
|
14
|
+
"dev": "node -r ts-node/register --env-file=.env examples/main.ts",
|
|
15
|
+
"lint": "eslint \"src/**/*.ts\" --report-unused-disable-directives --max-warnings 0",
|
|
16
|
+
"lint:fix": "eslint \"src/**/*.ts\" --fix",
|
|
17
|
+
"format": "prettier --write \"src/**/*.ts\" \"examples/**/*.ts\""
|
|
15
18
|
},
|
|
16
19
|
"keywords": [
|
|
17
20
|
"discord",
|
|
@@ -25,9 +28,14 @@
|
|
|
25
28
|
"ws": "^8.18.3"
|
|
26
29
|
},
|
|
27
30
|
"devDependencies": {
|
|
28
|
-
"@types/node": "^24.
|
|
31
|
+
"@types/node": "^24.6.0",
|
|
29
32
|
"@types/ws": "^8.18.1",
|
|
33
|
+
"eslint": "^9.36.0",
|
|
34
|
+
"eslint-config-prettier": "^10.1.8",
|
|
35
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
36
|
+
"prettier": "^3.6.2",
|
|
30
37
|
"ts-node": "^10.9.2",
|
|
31
|
-
"typescript": "^5.9.2"
|
|
38
|
+
"typescript": "^5.9.2",
|
|
39
|
+
"typescript-eslint": "^8.45.0"
|
|
32
40
|
}
|
|
33
41
|
}
|