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.
- package/LICENSE +21 -0
- package/README.md +864 -0
- package/index.js +13 -0
- package/package.json +28 -0
- package/src/classes/Actions/Awaiter.js +202 -0
- package/src/classes/Actions/Channel.js +73 -0
- package/src/classes/Actions/Direct.js +263 -0
- package/src/classes/Actions/Inventory.js +156 -0
- package/src/classes/Actions/Music.js +278 -0
- package/src/classes/Actions/Player.js +377 -0
- package/src/classes/Actions/Public.js +66 -0
- package/src/classes/Actions/Room.js +333 -0
- package/src/classes/Actions/Utils.js +29 -0
- package/src/classes/Actions/lib/AudioStreaming.js +447 -0
- package/src/classes/Caches/MovementCache.js +357 -0
- package/src/classes/Handlers/AxiosErrorHandler.js +68 -0
- package/src/classes/Handlers/ErrorHandler.js +65 -0
- package/src/classes/Handlers/EventHandlers.js +259 -0
- package/src/classes/Handlers/WebSocketHandlers.js +54 -0
- package/src/classes/Managers/ChannelManager.js +303 -0
- package/src/classes/Managers/DanceFloorManagers.js +509 -0
- package/src/classes/Managers/Helpers/CleanupManager.js +130 -0
- package/src/classes/Managers/Helpers/LoggerManager.js +171 -0
- package/src/classes/Managers/Helpers/MetricsManager.js +83 -0
- package/src/classes/Managers/Networking/ConnectionManager.js +259 -0
- package/src/classes/Managers/Networking/CooldownManager.js +516 -0
- package/src/classes/Managers/Networking/EventsManager.js +64 -0
- package/src/classes/Managers/Networking/KeepAliveManager.js +109 -0
- package/src/classes/Managers/Networking/MessageHandler.js +110 -0
- package/src/classes/Managers/Networking/Request.js +329 -0
- package/src/classes/Managers/PermissionManager.js +288 -0
- package/src/classes/WebApi/Category/Grab.js +98 -0
- package/src/classes/WebApi/Category/Item.js +347 -0
- package/src/classes/WebApi/Category/Post.js +154 -0
- package/src/classes/WebApi/Category/Room.js +137 -0
- package/src/classes/WebApi/Category/User.js +88 -0
- package/src/classes/WebApi/webapi.js +52 -0
- package/src/constants/TypesConstants.js +89 -0
- package/src/constants/WebSocketConstants.js +80 -0
- package/src/core/Highrise.js +123 -0
- package/src/core/HighriseWebsocket.js +228 -0
- package/src/utils/ConvertSvgToPng.js +51 -0
- package/src/utils/ModelPool.js +160 -0
- package/src/utils/Models.js +128 -0
- package/src/utils/versionCheck.js +27 -0
- package/src/validators/ConfigValidator.js +205 -0
- package/src/validators/ConnectionValidator.js +65 -0
- package/typings/index.d.ts +3820 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const convertAvatarToPNG = require("../../../utils/ConvertSvgToPng");
|
|
2
|
+
|
|
3
|
+
class UserCategory {
|
|
4
|
+
constructor(webapi, AxiosError) {
|
|
5
|
+
this.webapi = webapi;
|
|
6
|
+
this.logger = webapi.logger;
|
|
7
|
+
this.base = webapi.base;
|
|
8
|
+
this.AxiosErrorHandler = AxiosError
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async get(identifier) {
|
|
12
|
+
const method = 'bot.webapi.user.get'
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
if (!identifier || typeof identifier !== 'string') {
|
|
16
|
+
throw new TypeError('identifier must be a non-empty string');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const response = await this.base.get(`/users/${identifier}`);
|
|
20
|
+
if (response.status === 200 && response.data?.user) {
|
|
21
|
+
const { outfit, ...userData } = response.data.user;
|
|
22
|
+
return this._formatUserData(userData);
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (error instanceof TypeError) {
|
|
27
|
+
this.logger.error(method, `TypeError: ${error.message}`, { identifier }, error);
|
|
28
|
+
} else {
|
|
29
|
+
return this.AxiosErrorHandler.handle(error, this.logger, method, { identifier });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async getUserAvatar(identifier, imagePath = 'avatar.png') {
|
|
35
|
+
const method = `bot.webapi.user.getUserAvatar`
|
|
36
|
+
try {
|
|
37
|
+
if (!identifier || typeof identifier !== 'string') {
|
|
38
|
+
throw new TypeError('identifier must be a non-empty string');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (imagePath && !imagePath.endsWith('.png')) {
|
|
42
|
+
throw new TypeError(`imagePath extension must be .png`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const UserData = await this.get(identifier);
|
|
46
|
+
|
|
47
|
+
if (!UserData || !UserData.svg) {
|
|
48
|
+
throw new Error('User not found or no avatar available');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const svg = UserData.svg;
|
|
52
|
+
|
|
53
|
+
const status = await convertAvatarToPNG(svg, imagePath);
|
|
54
|
+
return status;
|
|
55
|
+
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if (error instanceof TypeError) {
|
|
58
|
+
this.logger.error(method, `TypeError: ${error.message}`, { identifier }, error);
|
|
59
|
+
} else {
|
|
60
|
+
this.logger.error(method, `Failed to get user avatar: ${error.message}`, { identifier }, error);
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
_formatUserData(userData) {
|
|
67
|
+
return {
|
|
68
|
+
id: userData.user_id,
|
|
69
|
+
username: userData.username,
|
|
70
|
+
bio: userData.bio || '',
|
|
71
|
+
joinedAt: this.webapi._formatDate(userData.created_at),
|
|
72
|
+
lastOnline: this.webapi._formatDate(userData.last_online),
|
|
73
|
+
followers: userData.follower_count || 0,
|
|
74
|
+
following: userData.following_count || 0,
|
|
75
|
+
friends: userData.friend_count || 0,
|
|
76
|
+
currentRoom: userData.current_room || '',
|
|
77
|
+
country: userData.country || '',
|
|
78
|
+
crew: userData.crew || '',
|
|
79
|
+
voiceEnabled: userData.voice_enabled || false,
|
|
80
|
+
discordConnected: userData.discord_connected || false,
|
|
81
|
+
avatar: userData.profile_photo || '',
|
|
82
|
+
icon: userData.photo_id || '',
|
|
83
|
+
svg: userData.avatar_svg || ''
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = UserCategory
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const axios = require('axios')
|
|
2
|
+
const UserAgent = require('user-agents')
|
|
3
|
+
const { AxiosErrorHandler } = require('../Handlers/AxiosErrorHandler')
|
|
4
|
+
const UserCategory = require('./Category/User')
|
|
5
|
+
const RoomCategory = require('./Category/Room')
|
|
6
|
+
const PostCategory = require('./Category/Post')
|
|
7
|
+
const ItemCategory = require('./Category/Item')
|
|
8
|
+
const GrabCategory = require('./Category/Grab')
|
|
9
|
+
|
|
10
|
+
class WebApi {
|
|
11
|
+
constructor(bot) {
|
|
12
|
+
this.bot = bot
|
|
13
|
+
this.logger = bot.utils.logger
|
|
14
|
+
this.base = axios.create({
|
|
15
|
+
baseURL: 'https://webapi.highrise.game',
|
|
16
|
+
timeout: 30000,
|
|
17
|
+
headers: {
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
'User-Agent': new UserAgent().toString()
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
this.user = new UserCategory(this, AxiosErrorHandler);
|
|
24
|
+
this.room = new RoomCategory(this, AxiosErrorHandler);
|
|
25
|
+
this.post = new PostCategory(this, AxiosErrorHandler);
|
|
26
|
+
this.item = new ItemCategory(this, AxiosErrorHandler);
|
|
27
|
+
this.grab = new GrabCategory(this, AxiosErrorHandler);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_formatDate(dateString) {
|
|
31
|
+
if (!dateString) return null;
|
|
32
|
+
try {
|
|
33
|
+
const date = new Date(dateString);
|
|
34
|
+
return isNaN(date.getTime()) ? null : date.toISOString();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_formatInventory(inventory) {
|
|
41
|
+
if (!inventory || !Array.isArray(inventory.items)) return null;
|
|
42
|
+
return {
|
|
43
|
+
items: inventory.items.map(item => ({
|
|
44
|
+
itemId: item.item_id,
|
|
45
|
+
activePalette: item.active_palette || null,
|
|
46
|
+
accountBound: item.account_bound || false
|
|
47
|
+
}))
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = { WebApi };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const reactions = ['clap', 'heart', 'thumbs', 'wave', 'wink'];
|
|
2
|
+
const gold_bars = [1, 5, 10, 50, 100, 500, 1000, 5000, 10000]
|
|
3
|
+
const payment_methods = ["bot_wallet_only", "bot_wallet_priority", "user_wallet_only"]
|
|
4
|
+
const default_skin = [
|
|
5
|
+
{
|
|
6
|
+
type: 'clothing',
|
|
7
|
+
amount: 1,
|
|
8
|
+
id: 'hair_front-n_malenew05',
|
|
9
|
+
account_bound: false,
|
|
10
|
+
active_palette: 1
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
type: 'clothing',
|
|
14
|
+
amount: 1,
|
|
15
|
+
id: 'hair_back-n_malenew05',
|
|
16
|
+
account_bound: false,
|
|
17
|
+
active_palette: 1
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: 'clothing',
|
|
21
|
+
amount: 1,
|
|
22
|
+
id: 'body-flesh',
|
|
23
|
+
account_bound: false,
|
|
24
|
+
active_palette: 27
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: 'clothing',
|
|
28
|
+
amount: 1,
|
|
29
|
+
id: 'eye-n_basic2018malesquaresleepy',
|
|
30
|
+
account_bound: false,
|
|
31
|
+
active_palette: 7
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
type: 'clothing',
|
|
35
|
+
amount: 1,
|
|
36
|
+
id: 'eyebrow-n_basic2018newbrows07',
|
|
37
|
+
account_bound: false,
|
|
38
|
+
active_palette: 0
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: 'clothing',
|
|
42
|
+
amount: 1,
|
|
43
|
+
id: 'nose-n_basic2018newnose05',
|
|
44
|
+
account_bound: false,
|
|
45
|
+
active_palette: 0
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: 'clothing',
|
|
49
|
+
amount: 1,
|
|
50
|
+
id: 'mouth-basic2018chippermouth',
|
|
51
|
+
account_bound: false,
|
|
52
|
+
active_palette: -1
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: 'clothing',
|
|
56
|
+
amount: 1,
|
|
57
|
+
id: 'freckle-n_basic2018freckle04',
|
|
58
|
+
account_bound: false,
|
|
59
|
+
active_palette: 0
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
type: 'clothing',
|
|
63
|
+
amount: 1,
|
|
64
|
+
id: 'shirt-n_room32019denimjackethoodie',
|
|
65
|
+
account_bound: false,
|
|
66
|
+
active_palette: 0
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: 'clothing',
|
|
70
|
+
amount: 1,
|
|
71
|
+
id: 'pants-n_starteritems2019cuffedjeanswhite',
|
|
72
|
+
account_bound: false,
|
|
73
|
+
active_palette: 0
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
type: 'clothing',
|
|
77
|
+
amount: 1,
|
|
78
|
+
id: 'shoes-n_room32019socksneakersgrey',
|
|
79
|
+
account_bound: false,
|
|
80
|
+
active_palette: 0
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
module.exports = {
|
|
85
|
+
reactions,
|
|
86
|
+
gold_bars,
|
|
87
|
+
default_skin,
|
|
88
|
+
payment_methods
|
|
89
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const WebSocketConstants = {
|
|
2
|
+
// Connection
|
|
3
|
+
KEEPALIVE_INTERVAL: 15000,
|
|
4
|
+
MAX_RECONNECT_DELAY: 300000, // 5 minutes
|
|
5
|
+
RECONNECT_BACKOFF_FACTOR: 1.5,
|
|
6
|
+
DEFAULT_RECONNECT_DELAY: 5000,
|
|
7
|
+
DEFAULT_AUTO_RECONNECT: true,
|
|
8
|
+
MAX_RECONNECT_ATTEMPTS: 10,
|
|
9
|
+
RECONNECT_BACKOFF_FACTOR: 1.5,
|
|
10
|
+
|
|
11
|
+
// Logger Options
|
|
12
|
+
DEFAULT_LOGGER_OPTIONS: {
|
|
13
|
+
SHOW_TIMESTAMP: true,
|
|
14
|
+
SHOW_METHODS_NAME: true,
|
|
15
|
+
SHOW_COLORS: true
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
// Metrics
|
|
19
|
+
METRICS_UPDATE_INTERVAL: 5000,
|
|
20
|
+
INACTIVE_USER_THRESHOLD: 30000, // 30 seconds
|
|
21
|
+
|
|
22
|
+
// Request/Response
|
|
23
|
+
DEFAULT_TIMEOUT: 10000,
|
|
24
|
+
DEFAULT_MAX_RETRIES: 2,
|
|
25
|
+
DEFAULT_RETRY_DELAY: 100,
|
|
26
|
+
|
|
27
|
+
// Validation
|
|
28
|
+
TOKEN_LENGTH: 64,
|
|
29
|
+
ROOM_ID_LENGTH: 24,
|
|
30
|
+
|
|
31
|
+
// Endpoints
|
|
32
|
+
BASE_WS_URL: 'wss://highrise.game/web/botapi',
|
|
33
|
+
|
|
34
|
+
// Headers
|
|
35
|
+
HEADERS: {
|
|
36
|
+
API_TOKEN: 'api-token',
|
|
37
|
+
ROOM_ID: 'room-id',
|
|
38
|
+
CONTENT_TYPE: 'Content-Type',
|
|
39
|
+
USER_AGENT: 'User-Agent'
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// Events
|
|
43
|
+
DEFAULT_EVENTS: [
|
|
44
|
+
'SessionMetadata', 'ChatEvent', 'WhisperEvent',
|
|
45
|
+
'UserMovedEvent', 'UserJoinedEvent', 'UserLeftEvent',
|
|
46
|
+
'MessageEvent', 'TipReactionEvent', 'RoomModeratedEvent'
|
|
47
|
+
],
|
|
48
|
+
|
|
49
|
+
// Error Codes
|
|
50
|
+
ERROR_CODES: {
|
|
51
|
+
NORMAL_CLOSURE: 1000,
|
|
52
|
+
GOING_AWAY: 1001,
|
|
53
|
+
PROTOCOL_ERROR: 1002,
|
|
54
|
+
UNSUPPORTED_DATA: 1003,
|
|
55
|
+
NO_STATUS_RECEIVED: 1005,
|
|
56
|
+
ABNORMAL_CLOSURE: 1006,
|
|
57
|
+
INVALID_FRAME_PAYLOAD_DATA: 1007,
|
|
58
|
+
POLICY_VIOLATION: 1008,
|
|
59
|
+
MESSAGE_TOO_BIG: 1009,
|
|
60
|
+
MISSING_EXTENSION: 1010,
|
|
61
|
+
INTERNAL_ERROR: 1011,
|
|
62
|
+
SERVICE_RESTART: 1012,
|
|
63
|
+
TRY_AGAIN_LATER: 1013,
|
|
64
|
+
BAD_GATEWAY: 1014,
|
|
65
|
+
TLS_HANDSHAKE_FAILED: 1015
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// Closing Codes
|
|
69
|
+
CLOSE_CODES: {
|
|
70
|
+
NORMAL: 1000,
|
|
71
|
+
AUTH_FAILED: 4001,
|
|
72
|
+
INVALID_TOKEN: 4003,
|
|
73
|
+
INVALID_ROOM: 4004,
|
|
74
|
+
RATE_LIMITED: 4009,
|
|
75
|
+
BANNED: 4029,
|
|
76
|
+
SERVER_ERROR: 5000
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
module.exports = WebSocketConstants;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const HighriseWebsocket = require('highrise-core/src/core/HighriseWebsocket');
|
|
2
|
+
const { ConfigValidator } = require('highrise-core/src/validators/ConfigValidator');
|
|
3
|
+
const { checkVersion } = require('highrise-core/src/utils/versionCheck');
|
|
4
|
+
const WebSocketConstants = require('highrise-core/src/constants/WebSocketConstants')
|
|
5
|
+
const Utils = require('highrise-core/src/classes/Actions/Utils');
|
|
6
|
+
|
|
7
|
+
class Highrise extends HighriseWebsocket {
|
|
8
|
+
constructor(events, userOptions = {}) {
|
|
9
|
+
const userLoggerOptions = userOptions.LoggerOptions || {};
|
|
10
|
+
|
|
11
|
+
const options = {
|
|
12
|
+
LoggerOptions: {
|
|
13
|
+
showTimestamp: userLoggerOptions.showTimestamp !== undefined
|
|
14
|
+
? userLoggerOptions.showTimestamp
|
|
15
|
+
: WebSocketConstants.DEFAULT_LOGGER_OPTIONS.SHOW_TIMESTAMP,
|
|
16
|
+
|
|
17
|
+
showMethodName: userLoggerOptions.showMethodName !== undefined
|
|
18
|
+
? userLoggerOptions.showMethodName
|
|
19
|
+
: WebSocketConstants.DEFAULT_LOGGER_OPTIONS.SHOW_METHODS_NAME,
|
|
20
|
+
|
|
21
|
+
colors: userLoggerOptions.colors !== undefined
|
|
22
|
+
? userLoggerOptions.colors
|
|
23
|
+
: WebSocketConstants.DEFAULT_LOGGER_OPTIONS.SHOW_COLORS
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
autoReconnect: userOptions.autoReconnect !== undefined
|
|
27
|
+
? userOptions.autoReconnect
|
|
28
|
+
: WebSocketConstants.DEFAULT_AUTO_RECONNECT,
|
|
29
|
+
|
|
30
|
+
reconnectDelay: userOptions.reconnectDelay !== undefined
|
|
31
|
+
? userOptions.reconnectDelay
|
|
32
|
+
: WebSocketConstants.DEFAULT_RECONNECT_DELAY,
|
|
33
|
+
|
|
34
|
+
customRoles: Array.isArray(userOptions.customRoles)
|
|
35
|
+
? userOptions.customRoles
|
|
36
|
+
: [],
|
|
37
|
+
|
|
38
|
+
music: {
|
|
39
|
+
enabled: userOptions.music.enabled || false,
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const eventsValidation = ConfigValidator.validateEvents(events);
|
|
44
|
+
const optionsValidation = ConfigValidator.validateHighriseOptions(options);
|
|
45
|
+
|
|
46
|
+
if (!eventsValidation.success) {
|
|
47
|
+
throw new Error(`Events validation failed: ${eventsValidation.errors.join(', ')}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!optionsValidation.success) {
|
|
51
|
+
throw new Error(`Options validation failed: ${optionsValidation.errors.join(', ')}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
super(eventsValidation.validatedEvents, optionsValidation.validatedConfig);
|
|
55
|
+
|
|
56
|
+
this.utils = new Utils(this, {
|
|
57
|
+
...options.LoggerOptions,
|
|
58
|
+
customRoles: options.customRoles
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
this._setupPublicAPI();
|
|
62
|
+
|
|
63
|
+
if (options.music.enabled) {
|
|
64
|
+
try {
|
|
65
|
+
const MusicClass = require('../classes/Actions/Music');
|
|
66
|
+
this.music = new MusicClass(this, options.music)
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error('Failed to initialize MusicClass:', error);
|
|
69
|
+
this.music = null;
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
this.music = null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
checkVersion(this.utils.logger);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
_setupPublicAPI() {
|
|
79
|
+
const { MessageClass } = require('../classes/Actions/Public');
|
|
80
|
+
const { WhisperClass } = require('../classes/Actions/Public');
|
|
81
|
+
const { DirectClass } = require('../classes/Actions/Direct');
|
|
82
|
+
const { PlayerClass } = require('../classes/Actions/Player');
|
|
83
|
+
const { RoomClass } = require('../classes/Actions/Room');
|
|
84
|
+
const { ChannelClass } = require('../classes/Actions/Channel');
|
|
85
|
+
const { InventoryClass } = require('../classes/Actions/Inventory');
|
|
86
|
+
const { WebApi } = require('../classes/WebApi/webapi');
|
|
87
|
+
|
|
88
|
+
this.message = new MessageClass(this);
|
|
89
|
+
this.whisper = new WhisperClass(this);
|
|
90
|
+
this.direct = new DirectClass(this);
|
|
91
|
+
this.player = new PlayerClass(this);
|
|
92
|
+
this.room = new RoomClass(this);
|
|
93
|
+
this.inventory = new InventoryClass(this);
|
|
94
|
+
this.webapi = new WebApi(this);
|
|
95
|
+
this.channel = new ChannelClass(this);
|
|
96
|
+
|
|
97
|
+
this.cache = {
|
|
98
|
+
position: this.movementCache
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
login(token, roomId) {
|
|
103
|
+
const credentialsValidation = ConfigValidator.validateCredentials(token, roomId);
|
|
104
|
+
if (!credentialsValidation.success) {
|
|
105
|
+
throw new Error(`Login validation failed: ${credentialsValidation.errors.join(', ')}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.connect(token, roomId);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
disconnect() {
|
|
112
|
+
if (this.music) {
|
|
113
|
+
this.music.destroy();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.utils.DanceFloor.destroy();
|
|
117
|
+
this.channel.clear();
|
|
118
|
+
this.utils.permissions.clearAll()
|
|
119
|
+
super.disconnect(1000, 'Graceful shutdown');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = Highrise;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
const EventEmitter = require('events');
|
|
2
|
+
|
|
3
|
+
const ConnectionManager = require('highrise-core/src/classes/Managers/Networking/ConnectionManager');
|
|
4
|
+
const KeepAliveManager = require('highrise-core/src/classes/Managers/Networking/KeepAliveManager');
|
|
5
|
+
const MessageHandler = require('highrise-core/src/classes/Managers/Networking/MessageHandler');
|
|
6
|
+
const EventsManager = require('highrise-core/src/classes/Managers/Networking/EventsManager');
|
|
7
|
+
const { FireAndForgetSender, RequestResponseSender } = require('highrise-core/src/classes/Managers/Networking/Request');
|
|
8
|
+
|
|
9
|
+
const MetricsManager = require('highrise-core/src/classes/Managers/Helpers/MetricsManager');
|
|
10
|
+
const CleanupManager = require('highrise-core/src/classes/Managers/Helpers/CleanupManager');
|
|
11
|
+
|
|
12
|
+
const ChannelManager = require('highrise-core/src/classes/Managers/ChannelManager');
|
|
13
|
+
const ConnectionValidator = require('highrise-core/src/validators/ConnectionValidator');
|
|
14
|
+
const WebSocketHandlers = require('highrise-core/src/classes/Handlers/WebSocketHandlers');
|
|
15
|
+
|
|
16
|
+
const { DirectClass } = require('highrise-core/src/classes/Actions/Direct');
|
|
17
|
+
const { MovementCache } = require('highrise-core/src/classes/Caches/MovementCache');
|
|
18
|
+
const AwaitClass = require('highrise-core/src/classes/Actions/Awaiter');
|
|
19
|
+
const { Logger } = require('highrise-core/src/classes/Managers/Helpers/LoggerManager');
|
|
20
|
+
|
|
21
|
+
class HighriseWebsocket extends EventEmitter {
|
|
22
|
+
constructor(events, options = {}) {
|
|
23
|
+
super();
|
|
24
|
+
|
|
25
|
+
this.events = ConnectionValidator.validateEvents(events);
|
|
26
|
+
this.options = ConnectionValidator.validateOptions(options);
|
|
27
|
+
|
|
28
|
+
this._initializeProperties();
|
|
29
|
+
this._setupManagers();
|
|
30
|
+
this._setupErrorHandlers();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
_initializeProperties() {
|
|
34
|
+
this._connected = false;
|
|
35
|
+
this._info = {
|
|
36
|
+
user: { id: null, username: null },
|
|
37
|
+
room: { id: null, name: null, owner_id: null }
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_setupManagers() {
|
|
42
|
+
this.logger = new Logger(this, this.options.LoggerOptions || {});
|
|
43
|
+
|
|
44
|
+
this.await = new AwaitClass(this);
|
|
45
|
+
this.metrics = new MetricsManager();
|
|
46
|
+
this.cleanupManager = new CleanupManager();
|
|
47
|
+
this.eventsManager = new EventsManager(this)
|
|
48
|
+
this.connectionManager = new ConnectionManager(this, this.logger, this.options);
|
|
49
|
+
this.keepAliveManager = new KeepAliveManager(this.connectionManager, this.logger);
|
|
50
|
+
this._fireAndForget = new FireAndForgetSender(this);
|
|
51
|
+
this._requestResponse = new RequestResponseSender(this);
|
|
52
|
+
this.messageHandler = new MessageHandler(this);
|
|
53
|
+
this._channelManager = new ChannelManager(this);
|
|
54
|
+
|
|
55
|
+
this._direct = new DirectClass(this);
|
|
56
|
+
this._movementCache = new MovementCache();
|
|
57
|
+
|
|
58
|
+
this.cleanupManager.registerResource(this._movementCache);
|
|
59
|
+
this.cleanupManager.registerResource(this._fireAndForget);
|
|
60
|
+
this.cleanupManager.registerResource(this._requestResponse);
|
|
61
|
+
this.cleanupManager.registerResource(this._channelManager);
|
|
62
|
+
|
|
63
|
+
const metricsInterval = setInterval(() => this._updateMetrics(), 5000);
|
|
64
|
+
this.cleanupManager.registerInterval(metricsInterval);
|
|
65
|
+
|
|
66
|
+
this._setupEventListeners();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_setupEventListeners() {
|
|
70
|
+
this.cleanupManager.registerListener(
|
|
71
|
+
this.connectionManager,
|
|
72
|
+
'connected',
|
|
73
|
+
this._handleConnected.bind(this)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
this.cleanupManager.registerListener(
|
|
77
|
+
this.connectionManager,
|
|
78
|
+
'disconnected',
|
|
79
|
+
this._handleDisconnected.bind(this)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
this.cleanupManager.registerListener(
|
|
83
|
+
this.connectionManager,
|
|
84
|
+
'message',
|
|
85
|
+
(data) => this.messageHandler.handle(data)
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
this.cleanupManager.registerListener(
|
|
89
|
+
this.connectionManager,
|
|
90
|
+
'error',
|
|
91
|
+
(error) => this.emit('error', error)
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_setupErrorHandlers() {
|
|
96
|
+
this.webSocketHandlers = new WebSocketHandlers(this);
|
|
97
|
+
this.processHandlers = this.webSocketHandlers.setupErrorHandlers();
|
|
98
|
+
this.cleanupManager.registerResource({
|
|
99
|
+
cleanup: () => this.webSocketHandlers.removeErrorHandlers(this.processHandlers)
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
_handleConnected() {
|
|
104
|
+
this._connected = true;
|
|
105
|
+
this.keepAliveManager.start();
|
|
106
|
+
this.emit('connected');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
_handleDisconnected(details) {
|
|
110
|
+
this._connected = false;
|
|
111
|
+
this.keepAliveManager.stop();
|
|
112
|
+
this.emit('disconnected', details);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
connect(token, roomId) {
|
|
116
|
+
try {
|
|
117
|
+
ConnectionValidator.validateCredentials(token, roomId);
|
|
118
|
+
|
|
119
|
+
this._info.room.id = roomId;
|
|
120
|
+
this.connectionManager.connect(token, roomId, this.events);
|
|
121
|
+
|
|
122
|
+
} catch (error) {
|
|
123
|
+
this.logger.error('HighriseWebsocket.connect', `Connection failed ${error.message}`, {
|
|
124
|
+
tokenLength: token?.length,
|
|
125
|
+
roomIdLength: roomId?.length
|
|
126
|
+
}, error);
|
|
127
|
+
|
|
128
|
+
this.emit('error', error);
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
disconnect(code = 1000, reason = 'Manual disconnect') {
|
|
134
|
+
this.logger.info('HighriseWebsocket.disconnect', 'Disconnecting...');
|
|
135
|
+
|
|
136
|
+
this.connectionManager.disconnect(code, reason);
|
|
137
|
+
this.cleanupManager.cleanup();
|
|
138
|
+
|
|
139
|
+
this._connected = false;
|
|
140
|
+
this.logger.success('HighriseWebsocket.disconnect', 'Disconnected successfully');
|
|
141
|
+
this.emit('disconnected', { code, reason, manual: true });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
send(data) {
|
|
145
|
+
return this.connectionManager.send(data);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
isConnected() {
|
|
149
|
+
return this.connectionManager.isConnected();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
getMetrics() {
|
|
153
|
+
const cacheStats = this._movementCache.getStats();
|
|
154
|
+
const metrics = this.metrics.getSnapshot();
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
...metrics,
|
|
158
|
+
connected: this.isConnected(),
|
|
159
|
+
room: this._info.room.name || 'Unknown',
|
|
160
|
+
cache: {
|
|
161
|
+
users: cacheStats.totalUsers,
|
|
162
|
+
memory: this.metrics.formatBytes(cacheStats.memoryUsage),
|
|
163
|
+
active: cacheStats.activeUsers,
|
|
164
|
+
changes: cacheStats.changesProcessed
|
|
165
|
+
},
|
|
166
|
+
pendingReq: {
|
|
167
|
+
fireForget: this._fireAndForget.getPendingCount(),
|
|
168
|
+
reqRes: this._requestResponse.getPendingCount()
|
|
169
|
+
},
|
|
170
|
+
keepAlive: this.keepAliveManager.getStats(),
|
|
171
|
+
cleanup: this.cleanupManager.getStats()
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
configureSenders(config = {}) {
|
|
176
|
+
if (config.defaultTimeout !== undefined) {
|
|
177
|
+
this._requestResponse.setConfig({ defaultTimeout: config.defaultTimeout });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (config.maxRetries !== undefined) {
|
|
181
|
+
this._requestResponse.setConfig({ maxRetries: config.maxRetries });
|
|
182
|
+
this._fireAndForget.setRetryConfig({ maxRetries: config.maxRetries });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (config.retryDelay !== undefined) {
|
|
186
|
+
this._requestResponse.setConfig({ retryDelay: config.retryDelay });
|
|
187
|
+
this._fireAndForget.setRetryConfig({ retryDelay: config.retryDelay });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.logger.info('HighriseWebsocket.configureSenders', 'Sender configuration updated', config);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
getSenderStats() {
|
|
194
|
+
return {
|
|
195
|
+
fireAndForget: { pending: this._fireAndForget.getPendingCount() },
|
|
196
|
+
requestResponse: { pending: this._requestResponse.getPendingCount() }
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
_updateMetrics() {
|
|
201
|
+
const cacheStats = this._movementCache.getStats();
|
|
202
|
+
this.metrics.updateCacheStats(cacheStats);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
get info() {
|
|
206
|
+
return { ...this._info };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
get movementCache() {
|
|
210
|
+
return this._movementCache;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
get fireAndForget() {
|
|
214
|
+
return this._fireAndForget;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
get requestResponse() {
|
|
218
|
+
return this._requestResponse;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
destroy() {
|
|
222
|
+
this.disconnect();
|
|
223
|
+
this.removeAllListeners();
|
|
224
|
+
this.cleanupManager.cleanup();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
module.exports = HighriseWebsocket;
|