highrise.bot 1.1.2 → 1.1.4

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.
Files changed (29) hide show
  1. package/package.json +1 -1
  2. package/src/classes/Actions/Channel.js +21 -292
  3. package/src/classes/Actions/Direct.js +7 -7
  4. package/src/classes/Actions/Inventory.js +5 -5
  5. package/src/classes/Actions/Outfit.js +1 -1
  6. package/src/classes/Actions/Player.js +13 -13
  7. package/src/classes/Actions/Public.js +2 -2
  8. package/src/classes/Actions/Room.js +11 -11
  9. package/src/classes/Actions/Utils.js +3 -3
  10. package/src/classes/Caches/MovementCache.js +105 -183
  11. package/src/classes/Handlers/CommandHandler.js +204 -150
  12. package/src/classes/Handlers/EventHandlers.js +1 -1
  13. package/src/classes/Managers/Networking/ConnectionManager.js +2 -0
  14. package/src/classes/Managers/Networking/EventsManager.js +17 -181
  15. package/src/classes/Managers/Networking/MessageHandler.js +2 -2
  16. package/src/constants/WebSocketConstants.js +2 -2
  17. package/src/core/Highrise.js +4 -127
  18. package/src/core/HighriseWebsocket.js +19 -57
  19. package/src/validators/ConfigValidator.js +17 -73
  20. package/src/validators/ConnectionValidator.js +0 -18
  21. package/typings/index.d.ts +141 -906
  22. package/src/classes/Actions/Music.js +0 -243
  23. package/src/classes/Actions/lib/AudioStreaming.js +0 -695
  24. package/src/utils/Job.js +0 -130
  25. /package/src/classes/{Managers/Helpers → Helpers}/CleanupManager.js +0 -0
  26. /package/src/classes/{Managers/Helpers → Helpers}/HighriseError.js +0 -0
  27. /package/src/classes/{Managers/Helpers → Helpers}/HighriseResponse.js +0 -0
  28. /package/src/classes/{Managers/Helpers → Helpers}/LoggerManager.js +0 -0
  29. /package/src/classes/{Managers/Helpers → Helpers}/MetricsManager.js +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "highrise.bot",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Unofficial JavaScript SDK for the Highrise platform. Feature-complete WebSocket client with TypeScript support, built for production environments.",
5
5
  "keywords": [
6
6
  "highrise.bot",
@@ -1,327 +1,56 @@
1
- const crypto = require('crypto');
2
1
  const ErrorConstants = require('highrise.bot/src/constants/ErrorConstants');
3
2
 
4
3
  class ChannelClass {
5
4
  constructor(bot) {
6
5
  this.bot = bot;
7
6
  this.logger = bot.logger;
8
-
9
- this._messageHistory = [];
10
- this._maxHistory = 500;
11
- this._listeners = new Map();
12
- this._tagIndex = new Map();
13
7
 
14
8
  this.HighriseError = bot.HighriseError;
15
9
  this.HighriseResponse = bot.HighriseResponse;
10
+
16
11
  }
17
-
12
+
18
13
  async send(message, tags = []) {
19
- let storedMessage
20
-
21
14
  try {
22
15
  if (typeof message !== 'string' || message.trim().length === 0) {
23
16
  return this.HighriseError.invalidMessage(message).toResult();
24
17
  }
25
-
26
- const normalizedTags = this._normalizeTags(tags);
27
-
18
+
19
+ if (!Array.isArray(tags) || !tags.every(tag => typeof tag === 'string')) {
20
+ return new this.HighriseError(
21
+ ErrorConstants.CHANNEL.INVALID_TAGS,
22
+ `tags must be a valid strings array`,
23
+ { tags }
24
+ ).toResult();
25
+ }
26
+
27
+ const normalizedTags = tags;
28
+
28
29
  const payload = {
29
30
  _type: 'ChannelRequest',
30
31
  message: message,
31
- tags: normalizedTags,
32
- rid: crypto.randomUUID()
32
+ tags: normalizedTags
33
33
  };
34
-
35
- const response = await this.bot._fireAndForget.send(payload);
36
-
34
+
35
+ const response = await this.bot.fireAndForget.send(payload);
36
+
37
37
  if (response.success) {
38
- storedMessage = {
39
- id: crypto.randomUUID(),
38
+ const ChannelMessage = {
40
39
  message,
41
40
  tags: normalizedTags,
42
41
  timestamp: Date.now(),
43
- senderId: this.bot.info.user?.id || 'unknown',
44
42
  sent: true
45
43
  };
46
-
47
- this._storeMessage(storedMessage);
48
-
49
- return this.HighriseResponse.success('message', storedMessage);
50
-
44
+ return this.HighriseResponse.success('message', ChannelMessage);
51
45
  }
52
-
53
46
  } catch (error) {
54
47
  return new this.HighriseError(
55
- ErrorConstants.CHANNEL.LISTENER_ERROR,
48
+ ErrorConstants.HIGHRISE_API.MESSAGE_SEND_FAILED,
56
49
  `Failed to send channel message: ${error.message}`,
57
- { storedMessage }
50
+ { message, tags }
58
51
  ).toResult();
59
52
  }
60
53
  }
61
-
62
- on(tags, callback, options = {}) {
63
- let listener
64
-
65
- try {
66
- if (!callback || typeof callback !== 'function') {
67
- return new this.HighriseError(
68
- ErrorConstants.VALIDATION.INVALID_CHANNEL_CALLBACK,
69
- `callback must be a function`
70
- ).toResult();
71
- }
72
-
73
- const normalizedTags = this._normalizeTags(Array.isArray(tags) ? tags : [tags]);
74
-
75
- const listenerId = crypto.randomUUID();
76
- listener = {
77
- id: listenerId,
78
- callback,
79
- tags: normalizedTags,
80
- options: {
81
- once: options.once || false,
82
- }
83
- };
84
-
85
- this._listeners.set(listenerId, listener);
86
-
87
- normalizedTags.forEach(tag => {
88
- if (!this._tagIndex.has(tag)) {
89
- this._tagIndex.set(tag, new Set());
90
- }
91
- this._tagIndex.get(tag).add(listenerId);
92
- });
93
-
94
- return this.HighriseResponse.success('listener', {
95
- id: listenerId,
96
- tags: normalizedTags}
97
- );
98
-
99
- } catch (error) {
100
- return new this.HighriseError(
101
- ErrorConstants.CHANNEL.LISTENER_ERROR,
102
- `Failed to register listener: ${error.message}`,
103
- { listener }
104
- ).toResult();
105
- }
106
- }
107
-
108
- once(tags, callback) {
109
- return this.on(tags, callback, { once: true });
110
- }
111
-
112
- off(listenerId) {
113
-
114
- try {
115
- const listener = this._listeners.get(listenerId);
116
- if (!listener) {
117
- return new this.HighriseError(
118
- ErrorConstants.NOT_FOUND.LISTENER_NOT_FOUND,
119
- `Listener '${listenerId ? `${listenerId.substring(0, 12)}...` : 'EMPTY'}' Not found to be removed.`
120
- ).toResult();
121
- }
122
-
123
- listener.tags.forEach(tag => {
124
- const set = this._tagIndex.get(tag);
125
- if (set) {
126
- set.delete(listenerId);
127
- if (set.size === 0) {
128
- this._tagIndex.delete(tag);
129
- }
130
- }
131
- });
132
-
133
- this._listeners.delete(listenerId);
134
-
135
- return this.HighriseResponse.success('listenerId', listenerId);
136
-
137
- } catch (error) {
138
- return new this.HighriseError(
139
- ErrorConstants.CHANNEL.LISTENER_ERROR,
140
- `Failed to remove listener: ${error.message}`,
141
- { listenerId }
142
- ).toResult();
143
- }
144
- }
145
-
146
- offAll(tags) {
147
- const normalizedTags = this._normalizeTags(Array.isArray(tags) ? tags : [tags]);
148
-
149
- const listenersToRemove = new Set();
150
-
151
- normalizedTags.forEach(tag => {
152
- const listeners = this._tagIndex.get(tag);
153
- if (listeners) {
154
- listeners.forEach(listenerId => listenersToRemove.add(listenerId));
155
- }
156
- });
157
-
158
- let removed = 0;
159
- let failed = {
160
- count: 0,
161
- errors: []
162
- }
163
- listenersToRemove.forEach(listenerId => {
164
- const result = this.off(listenerId);
165
- if (result.success) {
166
- removed++;
167
- } else {
168
- failed.errors.push(result.error)
169
- failed.count++
170
- }
171
- });
172
-
173
- return this.HighriseResponse.success('removed', {
174
- count: removed,
175
- failed,
176
- tags: normalizedTags
177
- });
178
- }
179
-
180
- query(filter = {}) {
181
- const {
182
- tags = [],
183
- since = 0,
184
- until = Date.now(),
185
- limit = 50
186
- } = filter;
187
-
188
- const normalizedTags = this._normalizeTags(Array.isArray(tags) ? tags : [tags]);
189
-
190
- const results = [];
191
-
192
- for (const msg of this._messageHistory) {
193
- if (this._matchesFilter(msg, { tags: normalizedTags, since, until })) {
194
- results.push(msg);
195
- if (results.length >= limit) break;
196
- }
197
- }
198
-
199
- return this.HighriseResponse.success('query', {
200
- count: results.length,
201
- messages: results,
202
- filter
203
- });
204
- }
205
-
206
- stats() {
207
- return this.HighriseResponse.success('stats', {
208
- totalMessages: this._messageHistory.length,
209
- activeListeners: this._listeners.size,
210
- indexedTags: this._tagIndex.size
211
- });
212
- }
213
-
214
- storeIncomingMessage(senderId, message, tags) {
215
- try {
216
- const normalizedTags = this._normalizeTags(tags);
217
-
218
- const storedMsg = {
219
- id: crypto.randomUUID(),
220
- message,
221
- tags: normalizedTags,
222
- timestamp: Date.now(),
223
- senderId,
224
- sent: false
225
- };
226
-
227
- this._storeMessage(storedMsg);
228
- this._triggerListeners(storedMsg);
229
-
230
- return this.HighriseResponse.success('stored', {
231
- id: storedMsg.id,
232
- senderId: storedMsg.senderId,
233
- tags: storedMsg.tags,
234
- timestamp: storedMsg.timestamp
235
- });
236
-
237
- } catch (error) {
238
- this.logger.error('ChannelManager', 'Failed to store incoming message', { error: error.message });
239
-
240
- return new this.HighriseError(
241
- ErrorConstants.CHANNEL.LISTENER_ERROR,
242
- 'Failed to store incoming message',
243
- { error: error.message }
244
- ).toResult();
245
- }
246
- }
247
-
248
- _triggerListeners(message) {
249
- const matchedListeners = new Map();
250
-
251
- message.tags.forEach(tag => {
252
- const listeners = this._tagIndex.get(tag);
253
- if (listeners) {
254
- listeners.forEach(listenerId => {
255
- const listener = this._listeners.get(listenerId);
256
- if (listener && !matchedListeners.has(listenerId)) {
257
- const hasAllTags = listener.tags.every(t => message.tags.includes(t));
258
- if (hasAllTags) {
259
- matchedListeners.set(listenerId, listener);
260
- }
261
- }
262
- });
263
- }
264
- });
265
-
266
- matchedListeners.forEach((listener, listenerId) => {
267
- try {
268
- listener.callback(message);
269
-
270
- if (listener.options.once) {
271
- this.off(listenerId);
272
- }
273
-
274
- } catch (error) {
275
- this.logger.error('ChannelManager', 'Listener callback error', { listenerId, error: error.message });
276
- }
277
- });
278
- }
279
-
280
- _normalizeTags(tags) {
281
- const normalized = tags
282
- .map(tag => {
283
- if (typeof tag !== 'string') return '';
284
- return tag.toLowerCase()
285
- .trim()
286
- .replace(/[^a-z0-9_:.-]/g, '-')
287
- .replace(/-+/g, '-')
288
- .substring(0, 50);
289
- })
290
- .filter(tag => tag.length > 0)
291
- .filter((tag, index, self) => self.indexOf(tag) === index)
292
- .sort();
293
-
294
- return normalized.length > 0 ? normalized : ['global'];
295
- }
296
-
297
- _storeMessage(message) {
298
- this._messageHistory.unshift(message);
299
-
300
- if (this._messageHistory.length > this._maxHistory) {
301
- this._messageHistory.splice(this._maxHistory);
302
- }
303
- }
304
-
305
- _matchesFilter(message, filter) {
306
- const { tags = [], since = 0, until = Date.now() } = filter;
307
-
308
- if (message.timestamp < since || message.timestamp > until) {
309
- return false;
310
- }
311
-
312
- if (tags.length > 0) {
313
- const hasAllTags = tags.every(tag => message.tags.includes(tag));
314
- if (!hasAllTags) return false;
315
- }
316
-
317
- return true;
318
- }
319
-
320
- clear() {
321
- this._messageHistory = [];
322
- this._listeners.clear();
323
- this._tagIndex.clear();
324
- }
325
54
  }
326
55
 
327
56
  module.exports = { ChannelClass };
@@ -29,7 +29,7 @@ class DirectClass {
29
29
  type: 'text'
30
30
  };
31
31
 
32
- const response = await this.bot._fireAndForget.send(payload);
32
+ const response = await this.bot.fireAndForget.send(payload);
33
33
  return response;
34
34
  }
35
35
 
@@ -99,7 +99,7 @@ class DirectClass {
99
99
  type: 'text'
100
100
  };
101
101
 
102
- const response = await this.bot._fireAndForget.send(payload);
102
+ const response = await this.bot.fireAndForget.send(payload);
103
103
 
104
104
  if (response.success) {
105
105
  return this.HighriseResponse.success('broadcast', {
@@ -179,7 +179,7 @@ class DirectClass {
179
179
  payload.world_id = options.world_id;
180
180
  }
181
181
 
182
- const response = await this.bot._fireAndForget.send(payload);
182
+ const response = await this.bot.fireAndForget.send(payload);
183
183
 
184
184
  if (response.success) {
185
185
  return this.HighriseResponse.success('invites', {
@@ -251,7 +251,7 @@ class DirectClass {
251
251
  payload.world_id = options.world_id;
252
252
  }
253
253
 
254
- const response = await this.bot._fireAndForget.send(payload);
254
+ const response = await this.bot.fireAndForget.send(payload);
255
255
 
256
256
  if (response.success) {
257
257
  return this.HighriseResponse.success('invite', {
@@ -296,7 +296,7 @@ class DirectClass {
296
296
  not_joined,
297
297
  };
298
298
 
299
- const response = await this.bot._requestResponse.send(payload);
299
+ const response = await this.bot.requestResponse.send(payload);
300
300
 
301
301
  if (response.success) {
302
302
  return this.HighriseResponse.success('conversations', {
@@ -327,7 +327,7 @@ class DirectClass {
327
327
  conversation_id: conversation_id,
328
328
  };
329
329
 
330
- const response = await this.bot._fireAndForget.send(payload);
330
+ const response = await this.bot.fireAndForget.send(payload);
331
331
 
332
332
  if (response.success) {
333
333
  return this.HighriseResponse.success('left', {
@@ -367,7 +367,7 @@ class DirectClass {
367
367
  last_message_id,
368
368
  };
369
369
 
370
- const response = await this.bot._requestResponse.send(payload);
370
+ const response = await this.bot.requestResponse.send(payload);
371
371
  if (response.success) {
372
372
  return this.HighriseResponse.success('messages', {
373
373
  list: response.data.messages || [],
@@ -15,7 +15,7 @@ class InventoryClass {
15
15
  _type: 'GetInventoryRequest'
16
16
  };
17
17
 
18
- const response = await this.bot._requestResponse.send(payload);
18
+ const response = await this.bot.requestResponse.send(payload);
19
19
  if (response.success) {
20
20
  return this.HighriseResponse.success('items', {
21
21
  list: response.data.items || [],
@@ -37,7 +37,7 @@ class InventoryClass {
37
37
  _type: 'GetWalletRequest'
38
38
  };
39
39
 
40
- const response = await this.bot._requestResponse.send(payload);
40
+ const response = await this.bot.requestResponse.send(payload);
41
41
 
42
42
  if (response.success) {
43
43
  const walletData = (response.data.content || []).reduce((acc, item) => {
@@ -85,7 +85,7 @@ class InventoryClass {
85
85
  amount: amount
86
86
  };
87
87
 
88
- const response = await this.bot._requestResponse.send(payload);
88
+ const response = await this.bot.requestResponse.send(payload);
89
89
  if (response.success && response.data.result === 'insufficient_funds') {
90
90
  return new this.HighriseError(
91
91
  ErrorConstants.ECONOMY.INSUFFICIENT_FUNDS,
@@ -127,7 +127,7 @@ class InventoryClass {
127
127
  payment_method: payment_method
128
128
  };
129
129
 
130
- const response = await this.bot._requestResponse.send(payload);
130
+ const response = await this.bot.requestResponse.send(payload);
131
131
 
132
132
  if (response.success && response.data.result === 'insufficient_funds') {
133
133
  return new this.HighriseError(
@@ -169,7 +169,7 @@ class InventoryClass {
169
169
  item_id: item_id
170
170
  };
171
171
 
172
- const response = await this.bot._requestResponse.send(payload);
172
+ const response = await this.bot.requestResponse.send(payload);
173
173
  console.log(response.data)
174
174
 
175
175
  if (response.success && response.result === 'success') {
@@ -24,7 +24,7 @@ class OutfitClass {
24
24
  outfit: outfit
25
25
  };
26
26
 
27
- const response = await this.bot._fireAndForget.send(payload);
27
+ const response = await this.bot.fireAndForget.send(payload);
28
28
 
29
29
  if (response.success) {
30
30
  return this.HighriseResponse.success('outfit', {
@@ -33,7 +33,7 @@ class PlayerClass {
33
33
  destination: destination
34
34
  };
35
35
 
36
- const response = await this.bot._fireAndForget.send(payload);
36
+ const response = await this.bot.fireAndForget.send(payload);
37
37
 
38
38
  if (response.success) {
39
39
  return this.HighriseResponse.success('walked', {
@@ -74,7 +74,7 @@ class PlayerClass {
74
74
  anchor: anchor
75
75
  };
76
76
 
77
- const response = await this.bot._fireAndForget.send(payload);
77
+ const response = await this.bot.fireAndForget.send(payload);
78
78
 
79
79
  if (response.success) {
80
80
  return this.HighriseResponse.success('sat', {
@@ -119,7 +119,7 @@ class PlayerClass {
119
119
  destination: { x, y, z, facing }
120
120
  };
121
121
 
122
- const response = await this.bot._fireAndForget.send(payload);
122
+ const response = await this.bot.fireAndForget.send(payload);
123
123
 
124
124
  if (response.success) {
125
125
  return this.HighriseResponse.success('teleported', {
@@ -164,7 +164,7 @@ class PlayerClass {
164
164
  room_id: room_id
165
165
  };
166
166
 
167
- const response = await this.bot._fireAndForget.send(payload);
167
+ const response = await this.bot.fireAndForget.send(payload);
168
168
 
169
169
  if (response.success) {
170
170
  return this.HighriseResponse.success('transported', {
@@ -201,7 +201,7 @@ class PlayerClass {
201
201
  target_user_id: user_id
202
202
  };
203
203
 
204
- const response = await this.bot._fireAndForget.send(payload);
204
+ const response = await this.bot.fireAndForget.send(payload);
205
205
 
206
206
  if (response.success) {
207
207
  return this.HighriseResponse.success('emoted', {
@@ -246,7 +246,7 @@ class PlayerClass {
246
246
  target_user_id: user_id
247
247
  };
248
248
 
249
- const response = await this.bot._fireAndForget.send(payload);
249
+ const response = await this.bot.fireAndForget.send(payload);
250
250
 
251
251
  if (response.success) {
252
252
  return this.HighriseResponse.success('reacted', {
@@ -312,7 +312,7 @@ class PlayerClass {
312
312
  gold_bar: PayladGoldBars[gold_bar]
313
313
  };
314
314
 
315
- const response = await this.bot._fireAndForget.send(payload);
315
+ const response = await this.bot.fireAndForget.send(payload);
316
316
 
317
317
  if (response.success) {
318
318
  return this.HighriseResponse.success('tipped', {
@@ -383,7 +383,7 @@ class PlayerClass {
383
383
  user_id: user_id
384
384
  };
385
385
 
386
- const response = await this.bot._requestResponse.send(payload);
386
+ const response = await this.bot.requestResponse.send(payload);
387
387
  if (response.success) {
388
388
  return this.HighriseResponse.success('outfit', {
389
389
  user_id,
@@ -423,7 +423,7 @@ class PlayerClass {
423
423
  action_length: null
424
424
  };
425
425
 
426
- const response = await this.bot._fireAndForget.send(payload);
426
+ const response = await this.bot.fireAndForget.send(payload);
427
427
 
428
428
  if (response.success) {
429
429
  return this.HighriseResponse.success('kicked', {
@@ -471,7 +471,7 @@ class PlayerClass {
471
471
  action_length: duration
472
472
  };
473
473
 
474
- const response = await this.bot._fireAndForget.send(payload);
474
+ const response = await this.bot.fireAndForget.send(payload);
475
475
 
476
476
  if (response.success) {
477
477
  return this.HighriseResponse.success('banned', {
@@ -519,7 +519,7 @@ class PlayerClass {
519
519
  action_length: duration
520
520
  };
521
521
 
522
- const response = await this.bot._fireAndForget.send(payload);
522
+ const response = await this.bot.fireAndForget.send(payload);
523
523
 
524
524
  if (response.success) {
525
525
  return this.HighriseResponse.success('muted', {
@@ -550,7 +550,7 @@ class PlayerClass {
550
550
  action_length: 1
551
551
  };
552
552
 
553
- const response = await this.bot._fireAndForget.send(payload);
553
+ const response = await this.bot.fireAndForget.send(payload);
554
554
 
555
555
  if (response.success) {
556
556
  return this.HighriseResponse.success('unmuted', {
@@ -581,7 +581,7 @@ class PlayerClass {
581
581
  action_length: null
582
582
  };
583
583
 
584
- const response = await this.bot._fireAndForget.send(payload);
584
+ const response = await this.bot.fireAndForget.send(payload);
585
585
 
586
586
  if (response.success) {
587
587
  return this.HighriseResponse.success('unbanned', {
@@ -28,7 +28,7 @@ class MessageClass {
28
28
  message: msg
29
29
  };
30
30
 
31
- const response = await this.bot._fireAndForget.send(payload);
31
+ const response = await this.bot.fireAndForget.send(payload);
32
32
  return response
33
33
  }
34
34
 
@@ -100,7 +100,7 @@ class WhisperClass {
100
100
  whisper_target_id: user_id
101
101
  };
102
102
 
103
- const response = await this.bot._fireAndForget.send(payload);
103
+ const response = await this.bot.fireAndForget.send(payload);
104
104
  return response;
105
105
  }
106
106