@hieuxyz/rpc 1.0.6 → 1.0.7

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.
@@ -7,12 +7,20 @@ export interface ClientOptions {
7
7
  token: string;
8
8
  /** (Optional) Base URL of the image proxy service. */
9
9
  apiBaseUrl?: string;
10
+ /**
11
+ * (Optional) If true, the client will attempt to reconnect even after a normal close (code 1000).
12
+ * Defaults to false.
13
+ */
14
+ alwaysReconnect?: boolean;
10
15
  }
11
16
  /**
12
17
  * The main Client class for interacting with Discord Rich Presence.
13
18
  * This is the starting point for creating and managing your RPC state.
14
19
  * @example
15
- * const client = new Client({ token: "YOUR_DISCORD_TOKEN" });
20
+ * const client = new Client({
21
+ * token: "YOUR_DISCORD_TOKEN",
22
+ * alwaysReconnect: true // Keep the RPC alive no matter what
23
+ * });
16
24
  * await client.run();
17
25
  * client.rpc.setName("Visual Studio Code");
18
26
  * await client.rpc.build();
@@ -41,6 +49,7 @@ export declare class Client {
41
49
  /**
42
50
  * Close the connection to Discord Gateway.
43
51
  * Terminate RPC and clean up resources.
52
+ * If `alwaysReconnect` is true, the client will attempt to reconnect after this.
44
53
  */
45
54
  close(): void;
46
55
  }
@@ -9,7 +9,10 @@ const logger_1 = require("./utils/logger");
9
9
  * The main Client class for interacting with Discord Rich Presence.
10
10
  * This is the starting point for creating and managing your RPC state.
11
11
  * @example
12
- * const client = new Client({ token: "YOUR_DISCORD_TOKEN" });
12
+ * const client = new Client({
13
+ * token: "YOUR_DISCORD_TOKEN",
14
+ * alwaysReconnect: true // Keep the RPC alive no matter what
15
+ * });
13
16
  * await client.run();
14
17
  * client.rpc.setName("Visual Studio Code");
15
18
  * await client.rpc.build();
@@ -34,7 +37,9 @@ class Client {
34
37
  }
35
38
  this.token = options.token;
36
39
  this.imageService = new ImageService_1.ImageService(options.apiBaseUrl);
37
- this.websocket = new DiscordWebSocket_1.DiscordWebSocket(this.token);
40
+ this.websocket = new DiscordWebSocket_1.DiscordWebSocket(this.token, {
41
+ alwaysReconnect: options.alwaysReconnect ?? false,
42
+ });
38
43
  this.rpc = new HieuxyzRPC_1.HieuxyzRPC(this.websocket, this.imageService);
39
44
  }
40
45
  /**
@@ -51,8 +56,10 @@ class Client {
51
56
  /**
52
57
  * Close the connection to Discord Gateway.
53
58
  * Terminate RPC and clean up resources.
59
+ * If `alwaysReconnect` is true, the client will attempt to reconnect after this.
54
60
  */
55
61
  close() {
62
+ this.rpc.stopBackgroundRenewal();
56
63
  this.websocket.close();
57
64
  }
58
65
  }
@@ -1,4 +1,7 @@
1
1
  import { PresenceUpdatePayload } from './entities/types';
2
+ interface DiscordWebSocketOptions {
3
+ alwaysReconnect: boolean;
4
+ }
2
5
  /**
3
6
  * Manage WebSocket connections to Discord Gateway.
4
7
  * Handles low-level operations like heartbeating, identifying, and resuming.
@@ -10,6 +13,7 @@ export declare class DiscordWebSocket {
10
13
  private heartbeatInterval;
11
14
  private sessionId;
12
15
  private resumeGatewayUrl;
16
+ private options;
13
17
  private resolveReady;
14
18
  /**
15
19
  * A promise will be resolved when the Gateway connection is ready.
@@ -19,15 +23,18 @@ export declare class DiscordWebSocket {
19
23
  /**
20
24
  * Create a DiscordWebSocket instance.
21
25
  * @param token - Discord user token for authentication.
26
+ * @param options - Configuration options for the WebSocket client.
22
27
  * @throws {Error} If the token is invalid.
23
28
  */
24
- constructor(token: string);
29
+ constructor(token: string, options: DiscordWebSocketOptions);
30
+ private resetReadyPromise;
25
31
  private isTokenValid;
26
32
  /**
27
33
  * Initiate connection to Discord Gateway.
28
34
  * If there was a previous session, it will try to resume.
29
35
  */
30
36
  connect(): void;
37
+ private reconnect;
31
38
  private onMessage;
32
39
  private startHeartbeating;
33
40
  private identify;
@@ -45,3 +52,4 @@ export declare class DiscordWebSocket {
45
52
  private cleanupHeartbeat;
46
53
  private shouldReconnect;
47
54
  }
55
+ export {};
@@ -19,22 +19,29 @@ class DiscordWebSocket {
19
19
  heartbeatInterval = null;
20
20
  sessionId = null;
21
21
  resumeGatewayUrl = null;
22
+ options;
22
23
  resolveReady = () => { };
23
24
  /**
24
25
  * A promise will be resolved when the Gateway connection is ready.
25
26
  * and received the READY event.
26
27
  */
27
- readyPromise = new Promise(resolve => (this.resolveReady = resolve));
28
+ readyPromise;
28
29
  /**
29
30
  * Create a DiscordWebSocket instance.
30
31
  * @param token - Discord user token for authentication.
32
+ * @param options - Configuration options for the WebSocket client.
31
33
  * @throws {Error} If the token is invalid.
32
34
  */
33
- constructor(token) {
35
+ constructor(token, options) {
34
36
  if (!this.isTokenValid(token)) {
35
37
  throw new Error("Invalid token provided.");
36
38
  }
37
39
  this.token = token;
40
+ this.options = options;
41
+ this.readyPromise = new Promise(resolve => (this.resolveReady = resolve));
42
+ }
43
+ resetReadyPromise() {
44
+ this.readyPromise = new Promise(resolve => (this.resolveReady = resolve));
38
45
  }
39
46
  isTokenValid(token) {
40
47
  return /^[a-zA-Z0-9_-]{24}\.[a-zA-Z0-9_-]{6}\.[a-zA-Z0-9_-]{38}$/.test(token) || /^mfa\.[a-zA-Z0-9_-]{84}$/.test(token);
@@ -44,7 +51,9 @@ class DiscordWebSocket {
44
51
  * If there was a previous session, it will try to resume.
45
52
  */
46
53
  connect() {
54
+ this.resetReadyPromise();
47
55
  const url = this.resumeGatewayUrl || "wss://gateway.discord.gg/?v=10&encoding=json";
56
+ logger_1.logger.info(`Attempting to connect to ${url}...`);
48
57
  this.ws = new ws_1.default(url);
49
58
  this.ws.on('open', () => logger_1.logger.info(`Connected to Discord Gateway at ${url}.`));
50
59
  this.ws.on('message', this.onMessage.bind(this));
@@ -52,18 +61,26 @@ class DiscordWebSocket {
52
61
  logger_1.logger.warn(`Connection closed: ${code} - ${reason.toString()}`);
53
62
  this.cleanupHeartbeat();
54
63
  if (this.shouldReconnect(code)) {
55
- logger_1.logger.info("Trying to reconnect...");
56
- setTimeout(() => this.connect(), 350);
64
+ logger_1.logger.info("Attempting to reconnect...");
65
+ this.reconnect(code === 4004);
57
66
  }
58
67
  else {
59
- this.sessionId = null;
60
- this.resumeGatewayUrl = null;
68
+ logger_1.logger.info("Not attempting to reconnect based on close code and client options.");
61
69
  }
62
70
  });
63
71
  this.ws.on('error', (err) => {
64
72
  logger_1.logger.error(`WebSocket Error: ${err.message}`);
65
73
  });
66
74
  }
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
+ }
67
84
  onMessage(data) {
68
85
  const payload = JSON.parse(data.toString());
69
86
  if (payload.s) {
@@ -95,10 +112,13 @@ class DiscordWebSocket {
95
112
  logger_1.logger.info("Heartbeat acknowledged.");
96
113
  break;
97
114
  case OpCode_1.OpCode.INVALID_SESSION:
98
- logger_1.logger.warn("Invalid session. Re-identifying after 3 seconds.");
99
- this.sessionId = null;
100
- this.resumeGatewayUrl = null;
101
- setTimeout(() => this.connect(), 3000);
115
+ logger_1.logger.warn(`Invalid session received. Resumable: ${payload.d}`);
116
+ if (payload.d === false) {
117
+ this.ws?.close(4004, "Session not resumable");
118
+ }
119
+ else {
120
+ this.ws?.close(4000, "Session is resumable, retrying");
121
+ }
102
122
  break;
103
123
  case OpCode_1.OpCode.RECONNECT:
104
124
  logger_1.logger.info("Gateway requested reconnect. Closing and reconnecting.");
@@ -110,10 +130,21 @@ class DiscordWebSocket {
110
130
  }
111
131
  startHeartbeating(interval) {
112
132
  this.cleanupHeartbeat();
113
- this.heartbeatInterval = setInterval(() => {
114
- this.sendJson({ op: OpCode_1.OpCode.HEARTBEAT, d: this.sequence });
115
- logger_1.logger.info(`Heartbeat sent with sequence ${this.sequence}.`);
116
- }, interval);
133
+ setTimeout(() => {
134
+ if (this.ws?.readyState === ws_1.default.OPEN) {
135
+ this.sendJson({ op: OpCode_1.OpCode.HEARTBEAT, d: this.sequence });
136
+ logger_1.logger.info(`Initial heartbeat sent with sequence ${this.sequence}.`);
137
+ }
138
+ this.heartbeatInterval = setInterval(() => {
139
+ if (this.ws?.readyState !== ws_1.default.OPEN) {
140
+ logger_1.logger.warn("Heartbeat skipped: WebSocket is not open.");
141
+ this.cleanupHeartbeat();
142
+ return;
143
+ }
144
+ this.sendJson({ op: OpCode_1.OpCode.HEARTBEAT, d: this.sequence });
145
+ logger_1.logger.info(`Heartbeat sent with sequence ${this.sequence}.`);
146
+ }, interval);
147
+ }, interval * Math.random());
117
148
  }
118
149
  identify() {
119
150
  const identifyPayload = (0, identify_1.getIdentifyPayload)(this.token);
@@ -121,6 +152,11 @@ class DiscordWebSocket {
121
152
  logger_1.logger.info("Identify payload sent.");
122
153
  }
123
154
  resume() {
155
+ if (!this.sessionId || this.sequence === null) {
156
+ logger_1.logger.error("Attempted to resume without session ID or sequence. Falling back to identify.");
157
+ this.identify();
158
+ return;
159
+ }
124
160
  const resumePayload = {
125
161
  token: this.token,
126
162
  session_id: this.sessionId,
@@ -149,12 +185,7 @@ class DiscordWebSocket {
149
185
  * Close the WebSocket connection and clean up the resources.
150
186
  */
151
187
  close() {
152
- if (this.ws) {
153
- this.ws.close(1000, "Client closed connection");
154
- }
155
- this.cleanupHeartbeat();
156
- this.sessionId = null;
157
- this.resumeGatewayUrl = null;
188
+ this.ws?.close(1000, "Client closed connection");
158
189
  }
159
190
  cleanupHeartbeat() {
160
191
  if (this.heartbeatInterval) {
@@ -163,7 +194,15 @@ class DiscordWebSocket {
163
194
  }
164
195
  }
165
196
  shouldReconnect(code) {
166
- return code !== 1000 && code !== 4004;
197
+ const fatalErrorCodes = [4010, 4011, 4013, 4014];
198
+ if (fatalErrorCodes.includes(code)) {
199
+ logger_1.logger.error(`Fatal WebSocket error received (code: ${code}). Will not reconnect.`);
200
+ return false;
201
+ }
202
+ if (this.options.alwaysReconnect) {
203
+ return true;
204
+ }
205
+ return code !== 1000;
167
206
  }
168
207
  }
169
208
  exports.DiscordWebSocket = DiscordWebSocket;
@@ -18,7 +18,13 @@ export declare class HieuxyzRPC {
18
18
  private status;
19
19
  private applicationId;
20
20
  private platform;
21
+ /**
22
+ * Cache for resolved image assets to avoid re-uploading or re-fetching.
23
+ * Key: A unique string from RpcImage.getCacheKey().
24
+ * Value: The resolved asset key (e.g., "mp:attachments/...").
25
+ */
21
26
  private resolvedAssetsCache;
27
+ private renewalInterval;
22
28
  constructor(websocket: DiscordWebSocket, imageService: ImageService);
23
29
  private _toRpcImage;
24
30
  private sanitize;
@@ -99,6 +105,14 @@ export declare class HieuxyzRPC {
99
105
  * @returns {this}
100
106
  */
101
107
  setPlatform(platform: DiscordPlatform): this;
108
+ private getExpiryTime;
109
+ private renewAssetIfNeeded;
110
+ private startBackgroundRenewal;
111
+ /**
112
+ * Stops the background process that checks for asset renewal.
113
+ */
114
+ stopBackgroundRenewal(): void;
115
+ private resolveImage;
102
116
  private buildActivity;
103
117
  /**
104
118
  * Build the final Rich Presence payload and send it to Discord.
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HieuxyzRPC = void 0;
4
4
  const types_1 = require("../gateway/entities/types");
5
5
  const RpcImage_1 = require("./RpcImage");
6
+ const logger_1 = require("../utils/logger");
6
7
  /**
7
8
  * Class built for creating and managing Discord Rich Presence states.
8
9
  */
@@ -14,10 +15,17 @@ class HieuxyzRPC {
14
15
  status = 'online';
15
16
  applicationId = '1416676323459469363'; // Default ID, can be changed
16
17
  platform = 'desktop';
17
- resolvedAssetsCache = {};
18
+ /**
19
+ * Cache for resolved image assets to avoid re-uploading or re-fetching.
20
+ * Key: A unique string from RpcImage.getCacheKey().
21
+ * Value: The resolved asset key (e.g., "mp:attachments/...").
22
+ */
23
+ resolvedAssetsCache = new Map();
24
+ renewalInterval = null;
18
25
  constructor(websocket, imageService) {
19
26
  this.websocket = websocket;
20
27
  this.imageService = imageService;
28
+ this.startBackgroundRenewal();
21
29
  }
22
30
  _toRpcImage(source) {
23
31
  if (typeof source !== 'string') {
@@ -35,7 +43,7 @@ class HieuxyzRPC {
35
43
  }
36
44
  }
37
45
  catch (e) {
38
- console.warn(`Could not parse "${source}" into a valid URL. Treating as RawImage.`);
46
+ logger_1.logger.warn(`Could not parse "${source}" into a valid URL. Treating as RawImage.`);
39
47
  return new RpcImage_1.RawImage(source);
40
48
  }
41
49
  }
@@ -126,7 +134,6 @@ class HieuxyzRPC {
126
134
  this.assets.large_image = this._toRpcImage(source);
127
135
  if (text)
128
136
  this.assets.large_text = this.sanitize(text);
129
- delete this.resolvedAssetsCache.large_image;
130
137
  return this;
131
138
  }
132
139
  /**
@@ -139,7 +146,6 @@ class HieuxyzRPC {
139
146
  this.assets.small_image = this._toRpcImage(source);
140
147
  if (text)
141
148
  this.assets.small_text = this.sanitize(text);
142
- delete this.resolvedAssetsCache.small_image;
143
149
  return this;
144
150
  }
145
151
  /**
@@ -184,29 +190,88 @@ class HieuxyzRPC {
184
190
  this.platform = platform;
185
191
  return this;
186
192
  }
187
- async buildActivity() {
188
- if (this.assets.large_image && !this.resolvedAssetsCache.large_image) {
189
- this.resolvedAssetsCache.large_image = await this.assets.large_image.resolve(this.imageService);
193
+ getExpiryTime(assetKey) {
194
+ if (!assetKey.startsWith('mp:attachments'))
195
+ return null;
196
+ const urlPart = assetKey.substring(3);
197
+ try {
198
+ const parsedUrl = new URL(`https://cdn.discordapp.com/${urlPart}`);
199
+ const expiresTimestamp = parsedUrl.searchParams.get('ex');
200
+ if (expiresTimestamp) {
201
+ return parseInt(expiresTimestamp, 16) * 1000;
202
+ }
190
203
  }
191
- if (this.assets.small_image && !this.resolvedAssetsCache.small_image) {
192
- this.resolvedAssetsCache.small_image = await this.assets.small_image.resolve(this.imageService);
204
+ catch (e) {
205
+ logger_1.logger.error(`Could not parse asset URL for expiry check: ${assetKey}`);
193
206
  }
207
+ return null;
208
+ }
209
+ async renewAssetIfNeeded(cacheKey, assetKey) {
210
+ const expiryTimeMs = this.getExpiryTime(assetKey);
211
+ if (expiryTimeMs && expiryTimeMs < (Date.now() + 3600000)) {
212
+ logger_1.logger.info(`Asset ${cacheKey} is expiring soon. Renewing...`);
213
+ const assetId = assetKey.split('mp:attachments/')[1];
214
+ const newAsset = await this.imageService.renewImage(assetId);
215
+ if (newAsset) {
216
+ this.resolvedAssetsCache.set(cacheKey, newAsset);
217
+ return newAsset;
218
+ }
219
+ logger_1.logger.warn(`Failed to renew asset, will use the old one.`);
220
+ }
221
+ return assetKey;
222
+ }
223
+ startBackgroundRenewal() {
224
+ if (this.renewalInterval) {
225
+ clearInterval(this.renewalInterval);
226
+ }
227
+ this.renewalInterval = setInterval(async () => {
228
+ logger_1.logger.info("Running background asset renewal check...");
229
+ for (const [cacheKey, assetKey] of this.resolvedAssetsCache.entries()) {
230
+ await this.renewAssetIfNeeded(cacheKey, assetKey);
231
+ }
232
+ }, 600000);
233
+ }
234
+ /**
235
+ * Stops the background process that checks for asset renewal.
236
+ */
237
+ stopBackgroundRenewal() {
238
+ if (this.renewalInterval) {
239
+ clearInterval(this.renewalInterval);
240
+ this.renewalInterval = null;
241
+ logger_1.logger.info("Stopped background asset renewal process.");
242
+ }
243
+ }
244
+ async resolveImage(image) {
245
+ if (!image)
246
+ return undefined;
247
+ const cacheKey = image.getCacheKey();
248
+ let cachedAsset = this.resolvedAssetsCache.get(cacheKey);
249
+ if (cachedAsset) {
250
+ return await this.renewAssetIfNeeded(cacheKey, cachedAsset);
251
+ }
252
+ const resolvedAsset = await image.resolve(this.imageService);
253
+ if (resolvedAsset) {
254
+ this.resolvedAssetsCache.set(cacheKey, resolvedAsset);
255
+ }
256
+ return resolvedAsset;
257
+ }
258
+ async buildActivity() {
259
+ const large_image = await this.resolveImage(this.assets.large_image);
260
+ const small_image = await this.resolveImage(this.assets.small_image);
194
261
  const finalAssets = {
195
262
  large_text: this.assets.large_text,
196
263
  small_text: this.assets.small_text,
197
264
  };
198
- if (this.resolvedAssetsCache.large_image) {
199
- finalAssets.large_image = this.resolvedAssetsCache.large_image;
200
- }
201
- if (this.resolvedAssetsCache.small_image) {
202
- finalAssets.small_image = this.resolvedAssetsCache.small_image;
203
- }
265
+ if (large_image)
266
+ finalAssets.large_image = large_image;
267
+ if (small_image)
268
+ finalAssets.small_image = small_image;
204
269
  const finalActivity = { ...this.activity };
205
- finalActivity.assets = finalAssets;
270
+ finalActivity.assets = (large_image || small_image) ? finalAssets : undefined;
206
271
  finalActivity.application_id = this.applicationId;
207
272
  finalActivity.platform = this.platform;
208
273
  if (!finalActivity.name) {
209
- finalActivity.name = "Custom Status";
274
+ finalActivity.name = "hieuxyzRPC";
210
275
  }
211
276
  if (typeof finalActivity.type === 'undefined') {
212
277
  finalActivity.type = types_1.ActivityType.Playing;
@@ -22,4 +22,10 @@ export declare class ImageService {
22
22
  * @returns {Promise<string | undefined>} Asset key resolved or undefined if failed.
23
23
  */
24
24
  uploadImage(filePath: string, fileName: string): Promise<string | undefined>;
25
+ /**
26
+ * Requests a new signed URL for an expired or expiring attachment asset.
27
+ * @param assetId The asset ID part of the URL (e.g., "channel_id/message_id/filename.ext...")
28
+ * @returns {Promise<string | undefined>} The new asset key or undefined if it failed.
29
+ */
30
+ renewImage(assetId: string): Promise<string | undefined>;
25
31
  }
@@ -102,5 +102,24 @@ class ImageService {
102
102
  }
103
103
  return undefined;
104
104
  }
105
+ /**
106
+ * Requests a new signed URL for an expired or expiring attachment asset.
107
+ * @param assetId The asset ID part of the URL (e.g., "channel_id/message_id/filename.ext...")
108
+ * @returns {Promise<string | undefined>} The new asset key or undefined if it failed.
109
+ */
110
+ async renewImage(assetId) {
111
+ try {
112
+ const response = await this.apiClient.post('/renew', { asset_id: assetId });
113
+ if (response.data && response.data.id) {
114
+ logger_1.logger.info(`Successfully renewed asset: ${assetId}`);
115
+ return response.data.id;
116
+ }
117
+ }
118
+ catch (error) {
119
+ const err = error;
120
+ logger_1.logger.error(`Failed to renew asset ${assetId}: ${err.response?.data || err.message}`);
121
+ }
122
+ return undefined;
123
+ }
105
124
  }
106
125
  exports.ImageService = ImageService;
@@ -9,6 +9,11 @@ export declare abstract class RpcImage {
9
9
  * @returns {Promise<string | undefined>} Asset key has been resolved.
10
10
  */
11
11
  abstract resolve(imageService: ImageService): Promise<string | undefined>;
12
+ /**
13
+ * Gets a unique key for this image instance, used for caching.
14
+ * @returns {string} A unique identifier for the image source.
15
+ */
16
+ abstract getCacheKey(): string;
12
17
  }
13
18
  /**
14
19
  * Represents an image that already exists on Discord's servers (e.g., via proxy or previous upload).
@@ -17,6 +22,7 @@ export declare class DiscordImage extends RpcImage {
17
22
  private imageKey;
18
23
  constructor(imageKey: string);
19
24
  resolve(): Promise<string | undefined>;
25
+ getCacheKey(): string;
20
26
  }
21
27
  /**
22
28
  * Represents an image from an external URL.
@@ -25,6 +31,7 @@ export declare class ExternalImage extends RpcImage {
25
31
  private url;
26
32
  constructor(url: string);
27
33
  resolve(imageService: ImageService): Promise<string | undefined>;
34
+ getCacheKey(): string;
28
35
  }
29
36
  /**
30
37
  * Represents an image from the local file system.
@@ -35,6 +42,7 @@ export declare class LocalImage extends RpcImage {
35
42
  private fileName;
36
43
  constructor(filePath: string, fileName?: string);
37
44
  resolve(imageService: ImageService): Promise<string | undefined>;
45
+ getCacheKey(): string;
38
46
  }
39
47
  /**
40
48
  * Represents a resolved raw asset key.
@@ -44,4 +52,5 @@ export declare class RawImage extends RpcImage {
44
52
  private assetKey;
45
53
  constructor(assetKey: string);
46
54
  resolve(imageService: ImageService): Promise<string | undefined>;
55
+ getCacheKey(): string;
47
56
  }
@@ -53,6 +53,9 @@ class DiscordImage extends RpcImage {
53
53
  async resolve() {
54
54
  return this.imageKey.startsWith('mp:') ? this.imageKey : `mp:${this.imageKey}`;
55
55
  }
56
+ getCacheKey() {
57
+ return `discord:${this.imageKey}`;
58
+ }
56
59
  }
57
60
  exports.DiscordImage = DiscordImage;
58
61
  /**
@@ -67,6 +70,9 @@ class ExternalImage extends RpcImage {
67
70
  async resolve(imageService) {
68
71
  return imageService.getExternalUrl(this.url);
69
72
  }
73
+ getCacheKey() {
74
+ return `external:${this.url}`;
75
+ }
70
76
  }
71
77
  exports.ExternalImage = ExternalImage;
72
78
  /**
@@ -84,6 +90,9 @@ class LocalImage extends RpcImage {
84
90
  async resolve(imageService) {
85
91
  return imageService.uploadImage(this.filePath, this.fileName);
86
92
  }
93
+ getCacheKey() {
94
+ return `local:${this.filePath}`;
95
+ }
87
96
  }
88
97
  exports.LocalImage = LocalImage;
89
98
  /**
@@ -99,5 +108,8 @@ class RawImage extends RpcImage {
99
108
  async resolve(imageService) {
100
109
  return this.assetKey;
101
110
  }
111
+ getCacheKey() {
112
+ return `raw:${this.assetKey}`;
113
+ }
102
114
  }
103
115
  exports.RawImage = RawImage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hieuxyz/rpc",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "A Discord Rich Presence library for Node.js",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -21,7 +21,7 @@
21
21
  "author": "hieuxyz",
22
22
  "license": "ISC",
23
23
  "dependencies": {
24
- "axios": "^1.7.2",
24
+ "axios": "^1.12.2",
25
25
  "ws": "^8.18.3"
26
26
  },
27
27
  "devDependencies": {