vantiv.io 1.0.0

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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +864 -0
  3. package/index.js +13 -0
  4. package/package.json +28 -0
  5. package/src/classes/Actions/Awaiter.js +202 -0
  6. package/src/classes/Actions/Channel.js +73 -0
  7. package/src/classes/Actions/Direct.js +263 -0
  8. package/src/classes/Actions/Inventory.js +156 -0
  9. package/src/classes/Actions/Music.js +278 -0
  10. package/src/classes/Actions/Player.js +377 -0
  11. package/src/classes/Actions/Public.js +66 -0
  12. package/src/classes/Actions/Room.js +333 -0
  13. package/src/classes/Actions/Utils.js +29 -0
  14. package/src/classes/Actions/lib/AudioStreaming.js +447 -0
  15. package/src/classes/Caches/MovementCache.js +357 -0
  16. package/src/classes/Handlers/AxiosErrorHandler.js +68 -0
  17. package/src/classes/Handlers/ErrorHandler.js +65 -0
  18. package/src/classes/Handlers/EventHandlers.js +259 -0
  19. package/src/classes/Handlers/WebSocketHandlers.js +54 -0
  20. package/src/classes/Managers/ChannelManager.js +303 -0
  21. package/src/classes/Managers/DanceFloorManagers.js +509 -0
  22. package/src/classes/Managers/Helpers/CleanupManager.js +130 -0
  23. package/src/classes/Managers/Helpers/LoggerManager.js +171 -0
  24. package/src/classes/Managers/Helpers/MetricsManager.js +83 -0
  25. package/src/classes/Managers/Networking/ConnectionManager.js +259 -0
  26. package/src/classes/Managers/Networking/CooldownManager.js +516 -0
  27. package/src/classes/Managers/Networking/EventsManager.js +64 -0
  28. package/src/classes/Managers/Networking/KeepAliveManager.js +109 -0
  29. package/src/classes/Managers/Networking/MessageHandler.js +110 -0
  30. package/src/classes/Managers/Networking/Request.js +329 -0
  31. package/src/classes/Managers/PermissionManager.js +288 -0
  32. package/src/classes/WebApi/Category/Grab.js +98 -0
  33. package/src/classes/WebApi/Category/Item.js +347 -0
  34. package/src/classes/WebApi/Category/Post.js +154 -0
  35. package/src/classes/WebApi/Category/Room.js +137 -0
  36. package/src/classes/WebApi/Category/User.js +88 -0
  37. package/src/classes/WebApi/webapi.js +52 -0
  38. package/src/constants/TypesConstants.js +89 -0
  39. package/src/constants/WebSocketConstants.js +80 -0
  40. package/src/core/Highrise.js +123 -0
  41. package/src/core/HighriseWebsocket.js +228 -0
  42. package/src/utils/ConvertSvgToPng.js +51 -0
  43. package/src/utils/ModelPool.js +160 -0
  44. package/src/utils/Models.js +128 -0
  45. package/src/utils/versionCheck.js +27 -0
  46. package/src/validators/ConfigValidator.js +205 -0
  47. package/src/validators/ConnectionValidator.js +65 -0
  48. package/typings/index.d.ts +3820 -0
@@ -0,0 +1,259 @@
1
+ const ModelPool = require("highrise-core/src/utils/ModelPool");
2
+ const { User, Position, Message, AnchorPosition, SessionMetadata, Voice, Direct, HiddenChannel, Tip, RoomModerate } = require('highrise-core/src/utils/Models')
3
+
4
+ const modelPool = new ModelPool()
5
+
6
+ modelPool.registerModel('Tip', (data) => new Tip(data))
7
+ modelPool.registerModel('User', (data) => new User(data));
8
+ modelPool.registerModel('Voice', (data) => new Voice(data))
9
+ modelPool.registerModel('Direct', (data) => new Direct(data));
10
+ modelPool.registerModel('Message', (data) => new Message(data));
11
+ modelPool.registerModel('Position', (data) => new Position(data));
12
+ modelPool.registerModel('RoomModerate', (data) => new RoomModerate(data))
13
+ modelPool.registerModel('HiddenChannel', (data) => new HiddenChannel(data))
14
+ modelPool.registerModel('AnchorPosition', (data) => new AnchorPosition(data));
15
+ modelPool.registerModel('SessionMetadata', (data) => new SessionMetadata(data));
16
+
17
+ function safeEmit(server, eventName, ...args) {
18
+ try {
19
+ server.emit(eventName, ...args);
20
+ } catch (error) {
21
+ console.error(`[PlayerHandlers] Error emitting ${eventName}:`, error);
22
+ server.emit('handlerError', error, eventName);
23
+ }
24
+ }
25
+
26
+ function releaseModels(modelMap) {
27
+ if (!modelMap) return;
28
+
29
+ for (const [type, models] of Object.entries(modelMap)) {
30
+ if (Array.isArray(models)) {
31
+ modelPool.releaseBatch(type, models);
32
+ } else if (models) {
33
+ modelPool.release(type, models);
34
+ }
35
+ }
36
+ }
37
+
38
+ function HiddenChannelHandler(server, data) {
39
+ let hiddenChannel = null
40
+
41
+ try {
42
+ hiddenChannel = modelPool.get('HiddenChannel', data)
43
+
44
+ if (server._channelManager) {
45
+ server._channelManager.storeIncomingMessage(
46
+ hiddenChannel.sender_id,
47
+ hiddenChannel.message,
48
+ hiddenChannel.tags
49
+ );
50
+ }
51
+
52
+
53
+ safeEmit(server, 'Channel', hiddenChannel.sender_id, hiddenChannel.message, hiddenChannel.tags)
54
+ } catch (error) {
55
+ console.error('[HiddenChannelHandler] Error:', error);
56
+ server.emit('error', error);
57
+ } finally {
58
+ if (hiddenChannel) {
59
+ modelPool.release('HiddenChannel', hiddenChannel);
60
+ }
61
+ }
62
+ }
63
+
64
+ function SessionMetadataHandler(server, data) {
65
+ let sessionMetadata = null;
66
+
67
+ try {
68
+ server._info.user.id = data.user_id
69
+ server._info.room.owner_id = data.room_info.owner_id
70
+ server._info.room.name = data.room_info.room_name
71
+
72
+ server.logger.info('Highrise', `Bot is now Online in ${data.room_info.room_name}!\n`);
73
+
74
+ sessionMetadata = modelPool.get('SessionMetadata', data);
75
+ safeEmit(server, 'Ready', sessionMetadata);
76
+ } catch (error) {
77
+ console.error('[SessionMetadataHandler] Error:', error);
78
+ server.emit('error', error);
79
+ } finally {
80
+ if (sessionMetadata) {
81
+ modelPool.release('SessionMetadata', sessionMetadata);
82
+ }
83
+ }
84
+ }
85
+
86
+ function UserJoinedHandler(server, data) {
87
+ let user = null, position = null;
88
+
89
+ try {
90
+ user = modelPool.get('User', data);
91
+ position = modelPool.get('Position', data);
92
+
93
+ server._movementCache._update(user.id, user.username, position);
94
+ safeEmit(server, 'UserJoined', user, position);
95
+ } catch (error) {
96
+ console.error('[UserJoinHandler] Error:', error);
97
+ server.emit('error', error);
98
+ } finally {
99
+ releaseModels({
100
+ 'User': [user],
101
+ 'Position': [position]
102
+ });
103
+ }
104
+ }
105
+
106
+ function UserLeftHandler(server, data) {
107
+ let user = null;
108
+
109
+ try {
110
+ user = modelPool.get('User', data);
111
+ server._movementCache._remove(user.id);
112
+ safeEmit(server, 'UserLeft', user);
113
+ } catch (error) {
114
+ console.error('[UserLeftHandler] Error:', error);
115
+ server.emit('error', error);
116
+ } finally {
117
+ if (user) {
118
+ modelPool.release('User', user);
119
+ }
120
+ }
121
+ }
122
+
123
+ function MessageEventHandler(server, data) {
124
+ let message = null;
125
+ if (data.user?.id === server._info?.user?.id) return;
126
+
127
+ try {
128
+ message = modelPool.get('Message', data);
129
+ const eventName = data.whisper ? 'Whisper' : 'Chat';
130
+ safeEmit(server, eventName, message.user, message.message);
131
+ } catch (error) {
132
+ console.error('[MessageEventHandler] Error:', error);
133
+ server.emit('error', error);
134
+ } finally {
135
+ if (message) {
136
+ modelPool.release('Message', message);
137
+ }
138
+ }
139
+ }
140
+
141
+ async function VoiceEventHandler(server, data) {
142
+ let voice = null
143
+
144
+ try {
145
+ voice = modelPool.get('Voice', data)
146
+ if (voice.users.length === 0) voice.ended = true
147
+
148
+ safeEmit(server, 'Voice', voice.users, voice.seconds_left, voice.ended)
149
+ } catch (error) {
150
+ console.error('[VoiceEventHandler] Error:', error);
151
+ server.emit('error', error);
152
+ } finally {
153
+ modelPool.release('Voice', voice);
154
+ }
155
+ }
156
+
157
+ async function DirectEventHandler(server, data) {
158
+ let direct = null
159
+
160
+ if (data.user_id === server._info?.user?.id) return;
161
+
162
+ const response = await server._direct.messages.get(data.conversation_id)
163
+
164
+ try {
165
+ if (!response || !Array.isArray(response) || response.length === 0) {
166
+ server.logger.warn('DirectEventHandler', 'No messages in response', {
167
+ conversationId: data.conversation_id
168
+ });
169
+ return;
170
+ }
171
+
172
+ const message = response[0].content || '';
173
+ data.message = message;
174
+
175
+ direct = modelPool.get('Direct', data)
176
+ server.await._processEvent('direct',
177
+ { id: data.user_id, username: null },
178
+ data.message,
179
+ { id: data.conversation_id, is_new_conversation: data.is_new_conversation }
180
+ );
181
+ safeEmit(server, 'Direct', direct.user, direct.message, direct.conversation);
182
+ } catch (error) {
183
+ console.error('[DirectEventHandler] Error:', error);
184
+ server.emit('error', error);
185
+ } finally {
186
+ modelPool.release('Direct', direct);
187
+ }
188
+ }
189
+
190
+ function UserMovementHandler(server, data) {
191
+ let user = null, position = null, anchor = null;
192
+
193
+ try {
194
+ user = modelPool.get('User', data);
195
+ position = modelPool.get('Position', data);
196
+
197
+ const isAnchor = data.position?.entity_id;
198
+ if (isAnchor) {
199
+ anchor = modelPool.get('AnchorPosition', data);
200
+ }
201
+
202
+ const changed = server._movementCache._update(user.id, user.username, position, anchor);
203
+
204
+ if (changed) {
205
+ safeEmit(server, 'Movement', user, position, anchor);
206
+ }
207
+
208
+ } catch (error) {
209
+ console.error('[UserMovementHandler] Error:', error);
210
+ server.emit('error', error);
211
+ } finally {
212
+ releaseModels({
213
+ 'User': [user],
214
+ 'Position': [position],
215
+ 'AnchorPosition': [anchor]
216
+ });
217
+ }
218
+ }
219
+
220
+ function TipReactionHandler(server, data) {
221
+ let tip = null
222
+
223
+ try {
224
+ tip = modelPool.get('Tip', data)
225
+ safeEmit(server, 'Tip', tip.sender, tip.receiver, tip.item)
226
+ } catch (error) {
227
+ console.error('[TipReactionHandler] Error:', error);
228
+ server.emit('error', error);
229
+ } finally {
230
+ modelPool.release('Tip', tip);
231
+ }
232
+ }
233
+
234
+ function RoomModerateHandler(server, data) {
235
+ let roomModerate = null
236
+
237
+ try {
238
+ roomModerate = modelPool.get('RoomModerate', data)
239
+ safeEmit(server, 'Moderation', roomModerate.moderator, roomModerate.target, roomModerate.action)
240
+ } catch (error) {
241
+ console.error('[RoomModerateHandler] Error:', error);
242
+ server.emit('error', error);
243
+ } finally {
244
+ modelPool.release('RoomModerate', roomModerate);
245
+ }
246
+ }
247
+
248
+ module.exports = {
249
+ UserLeftHandler,
250
+ VoiceEventHandler,
251
+ UserJoinedHandler,
252
+ TipReactionHandler,
253
+ DirectEventHandler,
254
+ MessageEventHandler,
255
+ UserMovementHandler,
256
+ RoomModerateHandler,
257
+ HiddenChannelHandler,
258
+ SessionMetadataHandler,
259
+ }
@@ -0,0 +1,54 @@
1
+ const WebSocketConstants = require('highrise-core/src/constants/WebSocketConstants');
2
+
3
+ class WebSocketHandlers {
4
+ constructor(server) {
5
+ this.server = server;
6
+ this.logger = server.logger;
7
+ }
8
+
9
+ setupErrorHandlers() {
10
+ const handlers = {
11
+ uncaughtException: this._handleUncaughtException.bind(this),
12
+ unhandledRejection: this._handleUnhandledRejection.bind(this),
13
+ SIGINT: this._handleSIGINT.bind(this),
14
+ SIGTERM: this._handleSIGTERM.bind(this)
15
+ };
16
+
17
+ process.on('uncaughtException', handlers.uncaughtException);
18
+ process.on('unhandledRejection', handlers.unhandledRejection);
19
+ process.on('SIGINT', handlers.SIGINT);
20
+ process.on('SIGTERM', handlers.SIGTERM);
21
+
22
+ return handlers;
23
+ }
24
+
25
+ _handleUncaughtException(error) {
26
+ this.logger.error('WebSocketHandlers', `Uncaught exception: ${error.message}`);
27
+ }
28
+
29
+ _handleUnhandledRejection(reason, promise) {
30
+ this.logger.error('WebSocketHandlers', `Unhandled promise rejection: ${reason}`);
31
+ }
32
+
33
+ _handleSIGINT() {
34
+ this.logger.info('WebSocketHandlers', 'Received SIGINT, shutting down gracefully');
35
+ this.server.disconnect();
36
+ setTimeout(() => process.exit(0), 1000);
37
+ }
38
+
39
+ _handleSIGTERM() {
40
+ this.logger.info('WebSocketHandlers', 'Received SIGTERM, shutting down gracefully');
41
+ this.server.disconnect();
42
+ setTimeout(() => process.exit(0), 1000);
43
+ }
44
+
45
+ removeErrorHandlers(handlers) {
46
+ if (!handlers) return;
47
+
48
+ Object.entries(handlers).forEach(([event, handler]) => {
49
+ process.removeListener(event, handler);
50
+ });
51
+ }
52
+ }
53
+
54
+ module.exports = WebSocketHandlers;
@@ -0,0 +1,303 @@
1
+ const crypto = require('crypto');
2
+
3
+ class ChannelManager {
4
+ constructor(server) {
5
+ this.server = server;
6
+ this.logger = server.logger;
7
+
8
+ this._messageHistory = [];
9
+ this._maxHistory = 500;
10
+ this._listeners = new Map();
11
+ this._tagIndex = new Map();
12
+ }
13
+
14
+ async send(message, tags = []) {
15
+ const method = 'bot.channel.send';
16
+
17
+ try {
18
+ if (typeof message !== 'string' || message.length === 0) {
19
+ throw new TypeError('Message must be a non-empty string');
20
+ }
21
+
22
+ if (!Array.isArray(tags)) {
23
+ throw new TypeError('Tags must be an array');
24
+ }
25
+
26
+ const normalizedTags = this._normalizeTags(tags);
27
+
28
+ const payload = {
29
+ _type: 'ChannelRequest',
30
+ message: message,
31
+ tags: normalizedTags,
32
+ rid: crypto.randomUUID()
33
+ };
34
+
35
+ const response = await this.server._fireAndForget.send(payload);
36
+
37
+ if (response.success) {
38
+ this._storeMessage({
39
+ id: crypto.randomUUID(),
40
+ message,
41
+ tags: normalizedTags,
42
+ timestamp: Date.now(),
43
+ senderId: this.server.info.user?.id || 'unknown',
44
+ sent: true
45
+ });
46
+
47
+ this.logger.debug(method, 'Channel message sent', {
48
+ tags: normalizedTags,
49
+ messageLength: message.length
50
+ });
51
+
52
+ return true;
53
+ }
54
+
55
+ return false;
56
+
57
+ } catch (error) {
58
+ if (error instanceof TypeError) {
59
+ this.logger.error(method, `TypeError: ${error.message}`, { message, tags }, error);
60
+ } else {
61
+ this.logger.error(method, `Failed to send channel message: ${error.message}`,
62
+ { message, tags }, error);
63
+ }
64
+ return false;
65
+ }
66
+ }
67
+
68
+ on(tags, callback, options = {}) {
69
+ const method = 'bot.channel.on';
70
+
71
+ try {
72
+ if (!callback || typeof callback !== 'function') {
73
+ throw new TypeError('Callback must be a function');
74
+ }
75
+
76
+ const normalizedTags = this._normalizeTags(Array.isArray(tags) ? tags : [tags]);
77
+
78
+ const listenerId = crypto.randomUUID();
79
+ const listener = {
80
+ id: listenerId,
81
+ callback,
82
+ tags: normalizedTags,
83
+ options: {
84
+ once: options.once || false,
85
+ ...options
86
+ }
87
+ };
88
+
89
+ this._listeners.set(listenerId, listener);
90
+
91
+ normalizedTags.forEach(tag => {
92
+ if (!this._tagIndex.has(tag)) {
93
+ this._tagIndex.set(tag, new Set());
94
+ }
95
+ this._tagIndex.get(tag).add(listenerId);
96
+ });
97
+
98
+ this.logger.debug(method, 'Listener registered', {
99
+ listenerId,
100
+ tags: normalizedTags
101
+ });
102
+
103
+ return listenerId;
104
+
105
+ } catch (error) {
106
+ if (error instanceof TypeError) {
107
+ this.logger.error(method, `TypeError: ${error.message}`, { tags }, error);
108
+ } else {
109
+ this.logger.error(method, `Failed to register listener: ${error.message}`, { tags }, error);
110
+ }
111
+ return null;
112
+ }
113
+ }
114
+
115
+ once(tags, callback) {
116
+ return this.on(tags, callback, { once: true });
117
+ }
118
+
119
+ off(listenerId) {
120
+ const method = 'bot.channel.off';
121
+
122
+ try {
123
+ const listener = this._listeners.get(listenerId);
124
+ if (!listener) {
125
+ this.logger.warn(method, 'Listener not found', { listenerId });
126
+ return false;
127
+ }
128
+
129
+ listener.tags.forEach(tag => {
130
+ const set = this._tagIndex.get(tag);
131
+ if (set) {
132
+ set.delete(listenerId);
133
+ if (set.size === 0) {
134
+ this._tagIndex.delete(tag);
135
+ }
136
+ }
137
+ });
138
+
139
+ this._listeners.delete(listenerId);
140
+
141
+ this.logger.debug(method, 'Listener removed', { listenerId });
142
+ return true;
143
+
144
+ } catch (error) {
145
+ this.logger.error(method, `Failed to remove listener: ${error.message}`, { listenerId }, error);
146
+ return false;
147
+ }
148
+ }
149
+
150
+ offAll(tags) {
151
+ const normalizedTags = this._normalizeTags(Array.isArray(tags) ? tags : [tags]);
152
+
153
+ const listenersToRemove = new Set();
154
+
155
+ normalizedTags.forEach(tag => {
156
+ const listeners = this._tagIndex.get(tag);
157
+ if (listeners) {
158
+ listeners.forEach(listenerId => listenersToRemove.add(listenerId));
159
+ }
160
+ });
161
+
162
+ let removed = 0;
163
+ listenersToRemove.forEach(listenerId => {
164
+ if (this.off(listenerId)) {
165
+ removed++;
166
+ }
167
+ });
168
+
169
+ return removed;
170
+ }
171
+
172
+ query(filter = {}) {
173
+ const {
174
+ tags = [],
175
+ since = 0,
176
+ until = Date.now(),
177
+ limit = 50
178
+ } = filter;
179
+
180
+ const normalizedTags = this._normalizeTags(Array.isArray(tags) ? tags : [tags]);
181
+
182
+ const results = [];
183
+
184
+ for (const msg of this._messageHistory) {
185
+ if (this._matchesFilter(msg, { tags: normalizedTags, since, until })) {
186
+ results.push(msg);
187
+ if (results.length >= limit) break;
188
+ }
189
+ }
190
+
191
+ return results;
192
+ }
193
+
194
+ getStats() {
195
+ return {
196
+ totalMessages: this._messageHistory.length,
197
+ listeners: this._listeners.size
198
+ };
199
+ }
200
+
201
+ storeIncomingMessage(senderId, message, tags) {
202
+ try {
203
+ const normalizedTags = this._normalizeTags(tags);
204
+
205
+ const storedMsg = {
206
+ id: crypto.randomUUID(),
207
+ message,
208
+ tags: normalizedTags,
209
+ timestamp: Date.now(),
210
+ senderId,
211
+ sent: false
212
+ };
213
+
214
+ this._storeMessage(storedMsg);
215
+
216
+ this._triggerListeners(storedMsg);
217
+
218
+ } catch (error) {
219
+ this.logger.error('ChannelManager', 'Failed to store incoming message', { error: error.message });
220
+ }
221
+ }
222
+
223
+ _triggerListeners(message) {
224
+ const matchedListeners = new Map();
225
+
226
+ message.tags.forEach(tag => {
227
+ const listeners = this._tagIndex.get(tag);
228
+ if (listeners) {
229
+ listeners.forEach(listenerId => {
230
+ const listener = this._listeners.get(listenerId);
231
+ if (listener && !matchedListeners.has(listenerId)) {
232
+ const hasAllTags = listener.tags.every(t => message.tags.includes(t));
233
+ if (hasAllTags) {
234
+ matchedListeners.set(listenerId, listener);
235
+ }
236
+ }
237
+ });
238
+ }
239
+ });
240
+
241
+ matchedListeners.forEach((listener, listenerId) => {
242
+ try {
243
+ listener.callback(message);
244
+
245
+ if (listener.options.once) {
246
+ this.off(listenerId);
247
+ }
248
+
249
+ } catch (error) {
250
+ this.logger.error('ChannelManager', 'Listener callback error', { listenerId, error: error.message });
251
+ }
252
+ });
253
+ }
254
+
255
+ _normalizeTags(tags) {
256
+ const normalized = tags
257
+ .map(tag => {
258
+ if (typeof tag !== 'string') return '';
259
+ return tag.toLowerCase()
260
+ .trim()
261
+ .replace(/[^a-z0-9_:.-]/g, '-')
262
+ .replace(/-+/g, '-')
263
+ .substring(0, 50);
264
+ })
265
+ .filter(tag => tag.length > 0)
266
+ .filter((tag, index, self) => self.indexOf(tag) === index)
267
+ .sort();
268
+
269
+ return normalized.length > 0 ? normalized : ['global'];
270
+ }
271
+
272
+ _storeMessage(message) {
273
+ this._messageHistory.unshift(message);
274
+
275
+ if (this._messageHistory.length > this._maxHistory) {
276
+ this._messageHistory.splice(this._maxHistory);
277
+ }
278
+ }
279
+
280
+ _matchesFilter(message, filter) {
281
+ const { tags = [], since = 0, until = Date.now() } = filter;
282
+
283
+ if (message.timestamp < since || message.timestamp > until) {
284
+ return false;
285
+ }
286
+
287
+ if (tags.length > 0) {
288
+ const hasAllTags = tags.every(tag => message.tags.includes(tag));
289
+ if (!hasAllTags) return false;
290
+ }
291
+
292
+ return true;
293
+ }
294
+
295
+ clear() {
296
+ this._messageHistory = [];
297
+ this._listeners.clear();
298
+ this._tagIndex.clear();
299
+ this.logger.info('ChannelManager', 'Cleared channel history and listeners');
300
+ }
301
+ }
302
+
303
+ module.exports = ChannelManager;