fnbr
Version:
A library to interact with Epic Games' Fortnite HTTP and XMPP services
193 lines • 9.53 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const ws_1 = tslib_1.__importDefault(require("ws"));
const Base_1 = tslib_1.__importDefault(require("../Base"));
const enums_1 = require("../../resources/enums");
const AuthenticationMissingError_1 = tslib_1.__importDefault(require("../exceptions/AuthenticationMissingError"));
const Endpoints_1 = tslib_1.__importDefault(require("../../resources/Endpoints"));
const ReceivedFriendMessage_1 = tslib_1.__importDefault(require("../structures/friend/ReceivedFriendMessage"));
const PartyMessage_1 = tslib_1.__importDefault(require("../structures/party/PartyMessage"));
const STOMPConnectionTimeoutError_1 = tslib_1.__importDefault(require("../exceptions/STOMPConnectionTimeoutError"));
const STOMPMessage_1 = tslib_1.__importDefault(require("./STOMPMessage"));
const STOMPConnectionError_1 = tslib_1.__importDefault(require("../exceptions/STOMPConnectionError"));
/**
* Represents the client's EOS Connect STOMP manager (i.e. chat messages)
*/
class STOMP extends Base_1.default {
/**
* @param client The main client
*/
constructor(client) {
super(client);
this.connection = undefined;
this.pingInterval = undefined;
this.connectionId = undefined;
this.connectionRetryCount = 0;
}
/**
* Whether the internal websocket is connected
*/
get isConnected() {
return this.connection && this.connection.readyState === ws_1.default.OPEN;
}
/**
* Connect to the STOMP server
* @throws {AuthenticationMissingError} When there is no EOS auth to use for STOMP auth
* @throws {STOMPConnectionError} When the connection failed for any reason
*/
async connect() {
if (!this.client.auth.sessions.has(enums_1.AuthSessionStoreKey.FortniteEOS)) {
throw new AuthenticationMissingError_1.default(enums_1.AuthSessionStoreKey.FortniteEOS);
}
this.client.debug('[STOMP] Connecting...');
const connectionStartTime = Date.now();
this.connection = new ws_1.default(`wss://${Endpoints_1.default.EOS_STOMP}`, {
headers: {
Authorization: `Bearer ${this.client.auth.sessions.get(enums_1.AuthSessionStoreKey.FortniteEOS).accessToken}`,
'Sec-Websocket-Protocol': 'v10.stomp,v11.stomp,v12.stomp',
'Epic-Connect-Device-Id': ' ',
'Epic-Connect-Protocol': 'stomp',
},
});
return new Promise((res, rej) => {
const connectionTimeout = setTimeout(() => {
this.disconnect();
rej(new STOMPConnectionTimeoutError_1.default(this.client.config.stompConnectionTimeout));
}, this.client.config.stompConnectionTimeout);
this.connection.once('open', () => {
clearTimeout(connectionTimeout);
this.sendMessage({
command: 'CONNECT',
headers: {
'accept-version': '1.0,1.1,1.2',
'heart-beat': '35000,0',
},
});
this.registerEvents(res, rej, connectionStartTime);
});
this.connection.once('error', (err) => {
this.client.debug(`[STOMP] Connection failed: ${err.message}`);
clearTimeout(connectionTimeout);
rej(new STOMPConnectionError_1.default(err.message));
});
});
}
/**
* Registers the events for the STOMP connection
*/
registerEvents(resolve, reject, connectionStartTime) {
this.connection.on('close', async (code, reason) => {
this.disconnect();
if (this.connectionRetryCount < 2) {
this.client.debug('[STOMP] Disconnected, reconnecting in 5 seconds...');
this.connectionRetryCount += 1;
await new Promise((res) => setTimeout(res, 5000));
await this.connect();
}
else {
this.client.debug('[STOMP] Disconnected, retry limit reached');
this.connectionRetryCount = 0;
throw new STOMPConnectionError_1.default(`STOMP WS disconnected, retry limit reached. Reason: ${reason}`, code);
}
});
this.connection.on('message', async (d) => {
var _a;
const message = STOMPMessage_1.default.fromString(d.toString());
switch (message.command) {
case 'CONNECTED':
this.pingInterval = setInterval(() => this.connection.send('\n'), 35000);
this.sendMessage({
command: 'SUBSCRIBE',
headers: {
id: 'sub-0',
destination: `${this.client.config.eosDeploymentId}/account/${this.client.user.self.id}`,
},
});
break;
case 'MESSAGE':
{
if (!message.body)
break;
const data = JSON.parse(message.body);
switch (data.type) {
case 'core.connect.v1.connected':
this.client.debug(`[STOMP] Successfully connected (${((Date.now() - connectionStartTime) / 1000).toFixed(2)}s)`);
this.connectionId = data.connectionId;
this.connectionRetryCount = 0;
resolve();
break;
case 'core.connect.v1.connect-failed':
this.client.debug(`[STOMP] Connection failed: ${data.statusCode} - ${data.message}`);
reject(new STOMPConnectionError_1.default(data.message, data.statusCode));
break;
case 'social.chat.v1.NEW_WHISPER': {
const { senderId, body, time } = data.payload.message;
const friend = this.client.friend.list.get(senderId);
if (!friend || senderId === this.client.user.self.id)
return;
const friendMessage = new ReceivedFriendMessage_1.default(this.client, {
content: body !== null && body !== void 0 ? body : '',
author: friend,
id: data.id,
sentAt: new Date(time),
});
this.client.emit('friend:message', friendMessage);
break;
}
case 'social.chat.v1.NEW_MESSAGE': {
if (data.payload.conversation.type !== 'party')
return;
await this.client.partyLock.wait();
const { conversation: { conversationId }, message: { senderId, body, time } } = data.payload;
const partyId = conversationId.replace('p-', '');
if (!this.client.party || this.client.party.id !== partyId || senderId === this.client.user.self.id) {
return;
}
const authorMember = this.client.party.members.get(senderId);
if (!authorMember)
return;
const partyMessage = new PartyMessage_1.default(this.client, {
content: body !== null && body !== void 0 ? body : '',
author: authorMember,
sentAt: new Date(time),
id: data.id,
party: this.client.party,
});
this.client.emit('party:member:message', partyMessage);
break;
}
default:
this.client.debug(`[STOMP] Unknown message type: ${data.type} ${message.body}`);
}
}
break;
default:
this.client.debug(`[STOMP] Unknown command: ${message.command} ${(_a = message.body) !== null && _a !== void 0 ? _a : 'no body'}`);
}
});
}
/**
* Disconnects the STOMP client.
* Also performs a cleanup
*/
async disconnect() {
if (!this.connection)
return;
clearInterval(this.pingInterval);
this.pingInterval = undefined;
this.connection.removeAllListeners();
this.connection.close();
this.connection = undefined;
this.connectionId = undefined;
}
/**
* Sends a message to the STOMP server
* @param message The message to send
*/
sendMessage(message) {
this.connection.send(new STOMPMessage_1.default(message).toString());
}
}
exports.default = STOMP;
//# sourceMappingURL=STOMP.js.map