UNPKG

@fnlb-project/fnbr

Version:

A library to interact with Epic Games' Fortnite HTTP and XMPP services

179 lines 7.04 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const Endpoints_1 = tslib_1.__importDefault(require("../../resources/Endpoints")); const enums_1 = require("../../resources/enums"); const Base_1 = tslib_1.__importDefault(require("../Base")); const Util_1 = require("../util/Util"); // private scope const generateCustomCorrelationId = () => `EOS-${Date.now()}-${(0, Util_1.getUUID)()}`; /** * Represent's the client's chat manager (dm, party chat) via eos. */ class ChatManager extends Base_1.default { constructor() { super(...arguments); /** * DM conversations cache map (account id -> conversation id) */ this.dmConversations = new Map(); } /** * Whether the keypair for message signing exists */ get keypairExists() { return !!this.privateKey && !!this.publicKey; } /** * Whether the keypair has been registered on epic's servers */ get keypairRegistered() { return !!this.publicKeyData; } /** * Returns the chat namespace, this is the eos deployment id */ get namespace() { return this.client.config.eosDeploymentId; } /** * Sends a private message to the specified user * @param user the account id or displayname * @param message the message object * @returns the message id * @throws {UserNotFoundError} When the specified user was not found * @throws {EpicgamesAPIError} When the api request failed */ async whisperUser(user, message) { const accountId = await this.client.user.resolveId(user); const conversationId = await this.getDMConversationId(accountId); return this.sendMessageInConversation(conversationId, message, [accountId, this.client.user.self.id], enums_1.ConversationType.DirectMessage); } /** * Sends a message in the specified conversation (e.g. party chat) * @param conversationId the conversation id, usually `p-[PARTYID]` * @param message the message object * @param allowedRecipients the account ids, that should receive the message * @returns the message id * @throws {EpicgamesAPIError} */ async sendMessageInConversation(conversationId, message, allowedRecipients, conversationType) { const correlationId = generateCustomCorrelationId(); const { body, signature } = await this.createSignedMessage(conversationId, message.body, conversationType === enums_1.ConversationType.DirectMessage ? enums_1.SignedMessageType.Persistent : enums_1.SignedMessageType.Party); await this.client.http.epicgamesRequest({ method: 'POST', url: `${Endpoints_1.default.EOS_CHAT}/v1/public/${conversationType === enums_1.ConversationType.DirectMessage ? '_' : this.namespace}` + `/conversations/${conversationId}/messages?fromAccountId=${this.client.user.self.id}`, headers: { 'Content-Type': 'application/json', 'X-Epic-Correlation-ID': correlationId, }, data: { allowedRecipients, message: { body, }, isReportable: false, metadata: { TmV: '2', Pub: this.publicKeyData.jwt, Sig: signature, NPM: conversationType === enums_1.ConversationType.Party ? '1' : undefined, PlfNm: this.client.config.platform, PlfId: this.client.user.self.id, }, }, }, enums_1.AuthSessionStoreKey.FortniteEOS); return correlationId; } async createDMConversation(recepientId, createIfExists = false) { return this.client.http.epicgamesRequest({ method: 'POST', url: `${Endpoints_1.default.EOS_CHAT}/v1/public/_/conversations?createIfExists=${createIfExists}`, headers: { 'Content-Type': 'application/json', }, data: { title: '', type: 'dm', members: [this.client.user.self.id, recepientId], }, }, enums_1.AuthSessionStoreKey.FortniteEOS); } /** * Ensures that message signing is possible */ async ensureMessageSigning() { if (!this.keypairExists) { await this.generateKeypair(); } if (!this.keypairRegistered) { await this.registerKeypair(); } } /** * Resolves the conversation id for a dm with the specified user * @param recepientId The account id of the recepient * @returns The conversation id */ async getDMConversationId(recepientId) { if (this.dmConversations.has(recepientId)) { return this.dmConversations.get(recepientId); } const conversationData = await this.createDMConversation(recepientId); this.dmConversations.set(recepientId, conversationData.conversationId); return conversationData.conversationId; } /** * Signs a message for the specified conversation * @param conversationId The conversation id * @param content The message content * @param type The signed message type */ async createSignedMessage(conversationId, content, type) { await this.ensureMessageSigning(); const timestamp = Date.now(); const messageInfo = { mid: (0, Util_1.getUUID)(), sid: this.client.user.self.id, rid: conversationId, msg: content, tst: timestamp, seq: 1, rec: false, mts: [], cty: type, }; const body = Buffer.from(JSON.stringify(messageInfo), 'utf-8').toString('base64'); const messageToSign = Buffer.concat([Buffer.from(body, 'utf-8'), Buffer.from([0])]); const signature = await (0, Util_1.signCrossPlatformMessage)(this.privateKey, messageToSign); return { body, signature }; } /** * Generates a ed25519 keypair for message signing */ async generateKeypair() { const { privateKey, publicKeyB64 } = await (0, Util_1.generateCrossPlatformKeyPair)(); this.privateKey = privateKey; this.publicKey = publicKeyB64; } /** * Registers the public key on epic's servers */ async registerKeypair() { const publicKeyData = await this.client.http.epicgamesRequest({ method: 'POST', url: `${Endpoints_1.default.PUBLICKEY}/v2/publickey/`, headers: { 'Content-Type': 'application/json', }, data: { key: this.publicKey, algorithm: 'ed25519', }, }, enums_1.AuthSessionStoreKey.Fortnite); this.publicKeyData = publicKeyData; } } exports.default = ChatManager; //# sourceMappingURL=ChatManager.js.map