fnbr
Version:
A library to interact with Epic Games' Fortnite HTTP and XMPP services
415 lines • 18 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const collection_1 = require("@discordjs/collection");
const async_queue_1 = require("@sapphire/async-queue");
const util_1 = require("util");
const Endpoints_1 = tslib_1.__importDefault(require("../../../resources/Endpoints"));
const FriendNotFoundError_1 = tslib_1.__importDefault(require("../../exceptions/FriendNotFoundError"));
const PartyAlreadyJoinedError_1 = tslib_1.__importDefault(require("../../exceptions/PartyAlreadyJoinedError"));
const PartyMaxSizeReachedError_1 = tslib_1.__importDefault(require("../../exceptions/PartyMaxSizeReachedError"));
const PartyMemberNotFoundError_1 = tslib_1.__importDefault(require("../../exceptions/PartyMemberNotFoundError"));
const PartyPermissionError_1 = tslib_1.__importDefault(require("../../exceptions/PartyPermissionError"));
const ClientPartyMeta_1 = tslib_1.__importDefault(require("./ClientPartyMeta"));
const Party_1 = tslib_1.__importDefault(require("./Party"));
const PartyChat_1 = tslib_1.__importDefault(require("./PartyChat"));
const SentPartyInvitation_1 = tslib_1.__importDefault(require("./SentPartyInvitation"));
const enums_1 = require("../../../resources/enums");
const EpicgamesAPIError_1 = tslib_1.__importDefault(require("../../exceptions/EpicgamesAPIError"));
const deprecationNotOverXmppAnymore = 'Party Chat is not done over XMPP anymore, this function will be removed in a future version';
/**
* Represents a party that the client is a member of
*/
class ClientParty extends Party_1.default {
/**
* @param client The main client
* @param data The party's data
*/
constructor(client, data) {
super(client, data instanceof Party_1.default ? data.toObject() : data);
this.hiddenMemberIds = new Set();
this.pendingMemberConfirmations = new collection_1.Collection();
this.patchQueue = new async_queue_1.AsyncQueue();
this.chat = new PartyChat_1.default(this.client, this);
this.meta = new ClientPartyMeta_1.default(this, data instanceof Party_1.default ? data.meta.schema : data.meta);
}
/**
* Returns the client's party member
*/
get me() {
return this.members.get(this.client.user.self.id);
}
/**
* Whether the party is private
*/
get isPrivate() {
return this.config.privacy.partyType === 'Private';
}
/**
* Leaves this party
* @param createNew Whether a new party should be created
* @throws {EpicgamesAPIError}
*/
async leave(createNew = true) {
var _a;
this.client.partyLock.lock();
try {
await this.client.http.epicgamesRequest({
method: 'DELETE',
url: `${Endpoints_1.default.BR_PARTY}/parties/${this.id}/members/${(_a = this.me) === null || _a === void 0 ? void 0 : _a.id}`,
}, enums_1.AuthSessionStoreKey.Fortnite);
}
catch (e) {
this.client.partyLock.unlock();
throw e;
}
this.client.party = undefined;
this.client.partyLock.unlock();
if (createNew)
await this.client.createParty();
}
/**
* Sends a party patch to Epicgames' servers
* @param updated The updated schema
* @param deleted The deleted schema keys
* @throws {PartyPermissionError} You're not the leader of this party
* @throws {EpicgamesAPIError}
*/
async sendPatch(updated, deleted = []) {
await this.patchQueue.wait();
try {
await this.client.http.epicgamesRequest({
method: 'PATCH',
url: `${Endpoints_1.default.BR_PARTY}/parties/${this.id}`,
headers: {
'Content-Type': 'application/json',
},
data: {
config: {
join_confirmation: this.config.joinConfirmation,
joinability: this.config.joinability,
max_size: this.config.maxSize,
discoverability: this.config.discoverability,
},
meta: {
delete: deleted,
update: updated || this.meta.schema,
},
party_state_overridden: {},
party_privacy_type: this.config.joinability,
party_type: this.config.type,
party_sub_type: this.config.subType,
max_number_of_members: this.config.maxSize,
invite_ttl_seconds: this.config.inviteTtl,
revision: this.revision,
},
}, enums_1.AuthSessionStoreKey.Fortnite);
}
catch (e) {
if (e instanceof EpicgamesAPIError_1.default && e.code === 'errors.com.epicgames.social.party.stale_revision') {
this.revision = parseInt(e.messageVars[1], 10);
this.patchQueue.shift();
return this.sendPatch(updated);
}
this.patchQueue.shift();
if (e instanceof EpicgamesAPIError_1.default && e.code === 'errors.com.epicgames.social.party.party_change_forbidden') {
throw new PartyPermissionError_1.default();
}
throw e;
}
this.revision += 1;
this.patchQueue.shift();
return undefined;
}
/**
* Kicks a member from this party
* @param member The member that should be kicked
* @throws {PartyPermissionError} The client is not the leader of the party
* @throws {PartyMemberNotFoundError} The party member wasn't found
* @throws {EpicgamesAPIError}
*/
async kick(member) {
if (!this.me.isLeader)
throw new PartyPermissionError_1.default();
const partyMember = this.members.find((m) => m.displayName === member || m.id === member);
if (!partyMember)
throw new PartyMemberNotFoundError_1.default(member);
try {
await this.client.http.epicgamesRequest({
method: 'DELETE',
url: `${Endpoints_1.default.BR_PARTY}/parties/${this.id}/members/${partyMember.id}`,
}, enums_1.AuthSessionStoreKey.Fortnite);
}
catch (e) {
if (e instanceof EpicgamesAPIError_1.default && e.code === 'errors.com.epicgames.social.party.party_change_forbidden') {
throw new PartyPermissionError_1.default();
}
throw e;
}
}
/**
* Sends a party invitation to a friend
* @param friend The friend that will receive the invitation
* @throws {FriendNotFoundError} The user is not friends with the client
* @throws {PartyAlreadyJoinedError} The user is already a member of this party
* @throws {PartyMaxSizeReachedError} The party reached its max size
* @throws {EpicgamesAPIError}
*/
async invite(friend) {
const resolvedFriend = this.client.friend.list.find((f) => f.id === friend || f.displayName === friend);
if (!resolvedFriend)
throw new FriendNotFoundError_1.default(friend);
if (this.members.has(resolvedFriend.id))
throw new PartyAlreadyJoinedError_1.default();
if (this.size === this.maxSize)
throw new PartyMaxSizeReachedError_1.default();
let invite;
if (this.isPrivate) {
invite = await this.client.http.epicgamesRequest({
method: 'POST',
url: `${Endpoints_1.default.BR_PARTY}/parties/${this.id}/invites/${resolvedFriend.id}?sendPing=true`,
headers: {
'Content-Type': 'application/json',
},
data: {
'urn:epic:cfg:build-id_s': this.client.config.partyBuildId,
'urn:epic:conn:platform_s': this.client.config.platform,
'urn:epic:conn:type_s': 'game',
'urn:epic:invite:platformdata_s': '',
'urn:epic:member:dn_s': this.client.user.self.displayName,
},
}, enums_1.AuthSessionStoreKey.Fortnite);
}
else {
invite = await this.client.http.epicgamesRequest({
method: 'POST',
url: `${Endpoints_1.default.BR_PARTY}/user/${resolvedFriend.id}/pings/${this.client.user.self.id}`,
headers: {
'Content-Type': 'application/json',
},
data: {
'urn:epic:invite:platformdata_s': '',
},
}, enums_1.AuthSessionStoreKey.Fortnite);
}
return new SentPartyInvitation_1.default(this.client, this, this.client.user.self, resolvedFriend, invite);
}
/**
* Refreshes the member positions of this party
* @throws {PartyPermissionError} The client is not the leader of the party
* @throws {EpicgamesAPIError}
*/
async refreshSquadAssignments() {
if (!this.me.isLeader)
throw new PartyPermissionError_1.default();
await this.sendPatch({
'Default:SquadInformation_j': this.meta.refreshSquadAssignments(),
});
}
/**
* Sends a message to the party chat
* @param content The message that will be sent
*/
async sendMessage(content) {
return this.chat.send(content);
}
/**
* Ban a member from this party chat
* @param member The member that should be banned
* @deprecated This feature has been deprecated since epic moved chatting away from xmpp
*/
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars
async chatBan(member) {
const deprecatedFn = (0, util_1.deprecate)(() => { }, deprecationNotOverXmppAnymore);
return deprecatedFn();
}
/**
* Updates this party's privacy settings
* @param privacy The updated party privacy
* @param sendPatch Whether the updated privacy should be sent to epic's servers
* @throws {PartyPermissionError} The client is not the leader of the party
* @throws {EpicgamesAPIError}
*/
async setPrivacy(privacy, sendPatch = true) {
if (!this.me.isLeader)
throw new PartyPermissionError_1.default();
const updated = {};
const deleted = [];
const privacyMeta = this.meta.get('Default:PrivacySettings_j');
if (privacyMeta) {
updated['Default:PrivacySettings_j'] = this.meta.set('Default:PrivacySettings_j', {
PrivacySettings: {
...privacyMeta.PrivacySettings,
partyType: privacy.partyType,
bOnlyLeaderFriendsCanJoin: privacy.onlyLeaderFriendsCanJoin,
partyInviteRestriction: privacy.inviteRestriction,
},
});
}
updated['urn:epic:cfg:presence-perm_s'] = this.meta.set('urn:epic:cfg:presence-perm_s', privacy.presencePermission);
updated['urn:epic:cfg:accepting-members_b'] = this.meta.set('urn:epic:cfg:accepting-members_b', privacy.acceptingMembers);
updated['urn:epic:cfg:invite-perm_s'] = this.meta.set('urn:epic:cfg:invite-perm_s', privacy.invitePermission);
if (privacy.partyType === 'Private') {
deleted.push('urn:epic:cfg:not-accepting-members');
updated['urn:epic:cfg:not-accepting-members-reason_i'] = this.meta.set('urn:epic:cfg:not-accepting-members-reason_i', 7);
this.config.discoverability = 'INVITED_ONLY';
this.config.joinability = 'INVITE_AND_FORMER';
}
else {
deleted.push('urn:epic:cfg:not-accepting-members-reason_i');
this.config.discoverability = 'ALL';
this.config.joinability = 'OPEN';
}
this.meta.remove(deleted);
if (sendPatch)
await this.sendPatch(updated, deleted);
this.config.privacy = {
...this.config.privacy,
...privacy,
};
return { updated, deleted };
}
/**
* Sets this party's custom matchmaking key
* @param key The custom matchmaking key
* @throws {PartyPermissionError} The client is not the leader of the party
* @throws {EpicgamesAPIError}
*/
async setCustomMatchmakingKey(key) {
if (!this.me.isLeader)
throw new PartyPermissionError_1.default();
await this.sendPatch({
'Default:CustomMatchKey_s': this.meta.set('Default:CustomMatchKey_s', key || ''),
});
}
/**
* Promotes a party member
* @param member The member that should be promoted
* @throws {PartyPermissionError} The client is not the leader of the party
* @throws {PartyMemberNotFoundError} The party member wasn't found
* @throws {EpicgamesAPIError}
*/
async promote(member) {
if (!this.me.isLeader)
throw new PartyPermissionError_1.default();
const partyMember = this.members.find((m) => m.displayName === member || m.id === member);
if (!partyMember)
throw new PartyMemberNotFoundError_1.default(member);
try {
await this.client.http.epicgamesRequest({
method: 'POST',
url: `${Endpoints_1.default.BR_PARTY}/parties/${this.id}/members/${partyMember.id}/promote`,
}, enums_1.AuthSessionStoreKey.Fortnite);
}
catch (e) {
if (e instanceof EpicgamesAPIError_1.default && e.code === 'errors.com.epicgames.social.party.party_change_forbidden') {
throw new PartyPermissionError_1.default();
}
throw e;
}
}
/**
* Hides / Unhides a single party member
* @param member The member that should be hidden
* @param hide Whether the member should be hidden
* @throws {PartyPermissionError} The client is not the leader of the party
* @throws {PartyMemberNotFoundError} The party member wasn't found
* @throws {EpicgamesAPIError}
*/
async hideMember(member, hide = true) {
if (!this.me.isLeader)
throw new PartyPermissionError_1.default();
const partyMember = this.members.find((m) => m.displayName === member || m.id === member);
if (!partyMember)
throw new PartyMemberNotFoundError_1.default(member);
if (hide) {
this.hiddenMemberIds.add(partyMember.id);
}
else {
this.hiddenMemberIds.delete(partyMember.id);
}
await this.refreshSquadAssignments();
}
/**
* Hides / Unhides all party members except for the client
* @param hide Whether all members should be hidden
* @throws {PartyPermissionError} The client is not the leader of the party
* @throws {EpicgamesAPIError}
*/
async hideMembers(hide = true) {
if (!this.me.isLeader)
throw new PartyPermissionError_1.default();
if (hide) {
this.members.filter((m) => m.id !== this.me.id).forEach((m) => this.hiddenMemberIds.add(m.id));
}
else {
this.hiddenMemberIds.clear();
}
await this.refreshSquadAssignments();
}
/**
* Updates the party's playlist
* @param mnemonic The new mnemonic (Playlist id or island code, for example: playlist_defaultduo or 1111-1111-1111)
* @param regionId The new region id
* @param version The new version
* @param options Playlist options
* @throws {PartyPermissionError} The client is not the leader of the party
* @throws {EpicgamesAPIError}
*/
async setPlaylist(mnemonic, regionId, version, options) {
if (!this.me.isLeader)
throw new PartyPermissionError_1.default();
let regionIdData = this.meta.get('Default:RegionId_s');
if (regionId) {
regionIdData = this.meta.set('Default:RegionId_s', regionId);
}
let data = this.meta.get('Default:SelectedIsland_j');
data = this.meta.set('Default:SelectedIsland_j', {
...data,
SelectedIsland: {
...data.SelectedIsland,
linkId: {
mnemonic,
version: version !== null && version !== void 0 ? version : -1,
},
...options,
},
});
await this.sendPatch({
'Default:SelectedIsland_j': data,
'Default:RegionId_s': regionIdData,
});
}
/**
* Updates the squad fill status of this party
* @param fill Whether fill is enable or not
* @throws {PartyPermissionError} The client is not the leader of the party
* @throws {EpicgamesAPIError}
*/
async setSquadFill(fill = true) {
if (!this.me.isLeader)
throw new PartyPermissionError_1.default();
await this.sendPatch({
'Default:AthenaSquadFill_b': this.meta.set('Default:AthenaSquadFill_b', fill),
});
}
/**
* Updates the party's max member count
* @param maxSize The new party max size (1-16)
* @throws {PartyPermissionError} The client is not the leader of the party
* @throws {RangeError} The new max member size must be between 1 and 16 (inclusive) and more than the current member count
* @throws {EpicgamesAPIError}
*/
async setMaxSize(maxSize) {
if (!this.me.isLeader)
throw new PartyPermissionError_1.default();
if (maxSize < 1 || maxSize > 16)
throw new RangeError('The new max member size must be between 1 and 16 (inclusive)');
if (maxSize < this.size)
throw new RangeError('The new max member size must be higher than the current member count');
this.config.maxSize = maxSize;
await this.sendPatch(this.meta.schema);
}
}
exports.default = ClientParty;
//# sourceMappingURL=ClientParty.js.map