Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] Epic moved the Chat system from xmpp to eos connect and its own api #776

Merged
merged 8 commits into from
Aug 3, 2024
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@ export { default as InviterFriendshipsLimitExceededError } from './src/exception
export { default as MatchNotFoundError } from './src/exceptions/MatchNotFoundError';
export { default as OfferNotFoundError } from './src/exceptions/OfferNotFoundError';
export { default as PartyAlreadyJoinedError } from './src/exceptions/PartyAlreadyJoinedError';
export { default as PartyChatConversationNotFound } from './src/exceptions/PartyChatConversationNotFound';
export { default as PartyInvitationExpiredError } from './src/exceptions/PartyInvitationExpiredError';
export { default as PartyMaxSizeReachedError } from './src/exceptions/PartyMaxSizeReachedError';
export { default as PartyMemberNotFoundError } from './src/exceptions/PartyMemberNotFoundError';
3 changes: 1 addition & 2 deletions resources/structs.ts
Original file line number Diff line number Diff line change
@@ -1478,7 +1478,7 @@ export interface AuthSessionStore<K, V> extends Collection<K, V> {
*/
export interface ChatMessagePayload {
/**
* The message body, should not be empty and not exceeed the limit of 256 characters
* The message body, should not be empty and not exceed the limit of 256 characters. Please note that emojis count as 2 characters.
*/
body: string;
}
@@ -1563,4 +1563,3 @@ export type EOSConnectMessage =
| EOSConnectChatNewMsgMessage
| EOSConnectChatMemberLeftMessage
| EOSConnectChatNewWhisperMessage;

3 changes: 1 addition & 2 deletions src/Client.ts
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ import type {
} from '../resources/structs';

/**
* Represets the main client
* Represents the main client
*/
class Client extends EventEmitter {
/**
@@ -771,7 +771,6 @@ class Client extends EventEmitter {
}, newPrivacy.deleted);

this.partyLock.unlock();
await this.party.chat.join();
return undefined;
}

2 changes: 1 addition & 1 deletion src/exceptions/CreativeIslandNotFoundError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because a creative island does not exist
* Represents an error thrown because a creative island does not exist
*/
class CreativeIslandNotFoundError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/CreatorCodeNotFoundError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because a creator code does not exist
* Represents an error thrown because a creator code does not exist
*/
class CreatorCodeNotFoundError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/DuplicateFriendshipError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because a friendship already exists
* Represents an error thrown because a friendship already exists
*/
class DuplicateFriendshipError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/EpicgamesAPIError.ts
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ import type { AxiosRequestConfig } from 'axios';
import type { EpicgamesAPIErrorData } from '../../resources/httpResponses';

/**
* Represets an HTTP error from the Epicgames API
* Represents an HTTP error from the Epicgames API
*/
class EpicgamesAPIError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/EventTimeoutError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because an event timeout was exceeded
* Represents an error thrown because an event timeout was exceeded
*/
class EventTimeoutError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/FriendNotFoundError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because a friend does not exist
* Represents an error thrown because a friend does not exist
*/
class FriendNotFoundError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/FriendshipRequestAlreadySentError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because a friendship request has already been sent
* Represents an error thrown because a friendship request has already been sent
*/
class FriendshipRequestAlreadySentError extends Error {
/**
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because the friendship invitee reached their friendship requests limit
* Represents an error thrown because the friendship invitee reached their friendship requests limit
*/
class InviteeFriendshipRequestLimitExceededError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/InviteeFriendshipSettingsError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because the friendship invitee does disabled friendship requests
* Represents an error thrown because the friendship invitee does disabled friendship requests
*/
class InviteeFriendshipSettingsError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/InviteeFriendshipsLimitExceededError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because the friendship invitee reached their friendships limit
* Represents an error thrown because the friendship invitee reached their friendships limit
*/
class InviteeFriendshipsLimitExceededError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/InviterFriendshipsLimitExceededError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because the client reached its friendships limit
* Represents an error thrown because the client reached its friendships limit
*/
class InviterFriendshipsLimitExceededError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/MatchNotFoundError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because a match does not exist
* Represents an error thrown because a match does not exist
*/
class MatchNotFoundError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/OfferNotFoundError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because an offer does not exist (anymore)
* Represents an error thrown because an offer does not exist (anymore)
*/
class OfferNotFoundError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/PartyAlreadyJoinedError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because a member (or the client) already joined a party
* Represents an error thrown because a member (or the client) already joined a party
*/
class PartyAlreadyJoinedError extends Error {
constructor() {
12 changes: 12 additions & 0 deletions src/exceptions/PartyChatConversationNotFound.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Represents an error thrown because you tried to send a party chat message, but there is no chat yet, because you are the only person in the party
*/
class PartyChatConversationNotFound extends Error {
constructor() {
super();
this.name = 'PartyChatConversationNotFound';
this.message = 'There is no party chat conversation yet. You cannot send party chat messages when you are the only party member.';
}
}

export default PartyChatConversationNotFound;
2 changes: 1 addition & 1 deletion src/exceptions/PartyInvitationExpiredError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because the client does not have permission to perform a certain action
* Represents an error thrown because the client does not have permission to perform a certain action
*/
class PartyInvitationExpiredError extends Error {
constructor() {
2 changes: 1 addition & 1 deletion src/exceptions/PartyMaxSizeReachedError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because a party already reached its max member count
* Represents an error thrown because a party already reached its max member count
*/
class PartyMaxSizeReachedError extends Error {
constructor() {
2 changes: 1 addition & 1 deletion src/exceptions/PartyMemberNotFoundError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because a party member does not exist
* Represents an error thrown because a party member does not exist
*/
class PartyMemberNotFoundError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/PartyNotFoundError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because a party does not exist
* Represents an error thrown because a party does not exist
*/
class PartyNotFoundError extends Error {
constructor() {
2 changes: 1 addition & 1 deletion src/exceptions/PartyPermissionError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because the client does not have permission to perform a certain party related action
* Represents an error thrown because the client does not have permission to perform a certain party related action
*/
class PartyPermissionError extends Error {
constructor() {
2 changes: 1 addition & 1 deletion src/exceptions/SendMessageError.ts
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import type ClientParty from '../structures/party/ClientParty';
import type Friend from '../structures/friend/Friend';

/**
* Represets an error thrown because a user does not exist
* Represents an error thrown because a user does not exist
*/
class SendMessageError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/StatsPrivacyError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because a user set their stats to private
* Represents an error thrown because a user set their stats to private
*/
class StatsPrivacyError extends Error {
/**
2 changes: 1 addition & 1 deletion src/exceptions/UserNotFoundError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Represets an error thrown because a user does not exist
* Represents an error thrown because a user does not exist
*/
class UserNotFoundError extends Error {
/**
4 changes: 4 additions & 0 deletions src/managers/ChatManager.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
import { AuthSessionStoreKey } from '../../resources/enums';
import Base from '../Base';
import UserNotFoundError from '../exceptions/UserNotFoundError';
import EpicgamesAPIError from '../exceptions/EpicgamesAPIError';

Check failure on line 6 in src/managers/ChatManager.ts

GitHub Actions / Test Code (Node 16)

'EpicgamesAPIError' is defined but never used.
import type { ChatMessagePayload } from '../../resources/structs';

// private scope
@@ -24,6 +25,8 @@
* @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
*/
public async whisperUser(user: string, message: ChatMessagePayload) {
const accountId = await this.client.user.resolveId(user);
@@ -55,6 +58,7 @@
* @param message the message object
* @param allowedRecipients the account ids, that should receive the message
* @returns the message id
* @throws {EpicgamesAPIError} When the api request failed
*/
public async sendMessageInConversation(conversationId: string, message: ChatMessagePayload, allowedRecipients: string[]) {
const correlationId = generateCustomCorrelationId();
3 changes: 3 additions & 0 deletions src/stomp/EOSConnect.ts
Original file line number Diff line number Diff line change
@@ -51,6 +51,9 @@ class EOSConnect extends Base {

/**
* connect to the eos connect stomp server
* @throws {AuthenticationMissingError} When there is no eos auth to use for stomp auth
* @throws {StompConnectionError} When the connection failed for any reason
* @throws {Error} When there was an error with the underlying websocket
*/
public async connect() {
if (!this.client.auth.sessions.has(AuthSessionStoreKey.FortniteEOS)) {
11 changes: 8 additions & 3 deletions src/structures/party/ClientParty.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Collection } from '@discordjs/collection';
import { AsyncQueue } from '@sapphire/async-queue';
import { deprecate } from 'util';
import Endpoints from '../../../resources/Endpoints';
import FriendNotFoundError from '../../exceptions/FriendNotFoundError';
import PartyAlreadyJoinedError from '../../exceptions/PartyAlreadyJoinedError';
@@ -21,6 +22,8 @@ import type {
import type PartyMember from './PartyMember';
import type Friend from '../friend/Friend';

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
*/
@@ -86,8 +89,6 @@ class ClientParty extends Party {
public async leave(createNew = true) {
this.client.partyLock.lock();

if (this.chat.isConnected) await this.chat.leave();

try {
await this.client.http.epicgamesRequest({
method: 'DELETE',
@@ -262,9 +263,13 @@ class ClientParty extends Party {
/**
* 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
public async chatBan(member: string) {
return this.chat.ban(member);
const deprecatedFn = deprecate(() => { }, deprecationNotOverXmppAnymore);

return deprecatedFn();
}

/**
64 changes: 35 additions & 29 deletions src/structures/party/PartyChat.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,40 @@
import { deprecate } from 'util';
import Base from '../../Base';
import AsyncLock from '../../util/AsyncLock';
import PartyMessage from './PartyMessage';
import PartyChatConversationNotFound from '../../exceptions/PartyChatConversationNotFound';
import type Client from '../../Client';
import type ClientParty from './ClientParty';
import type ClientPartyMember from './ClientPartyMember';

const deprecationNotOverXmppAnymore = 'Party Chat is not done over XMPP anymore, this function will be removed in a future version';

/**
* Represents a party's conversation
*/
class PartyChat extends Base {
/**
* The chat room's JID
* @deprecated since chat is not done over xmpp anymore, this property will always be an empty string
* @deprecated since chat is not done over xmpp anymore, this property will always be an empty string and will be removed in a future version
*/
public jid: string;

/**
* the party chats conversation id
* the party chat's conversation id
*/
public get conversationId() {
return `p-${this.party.id}`;
}

/**
* The client's chat room nickname
* @deprecated since chat is not done over xmpp anymore, this property will always be an empty string
* @deprecated since chat is not done over xmpp anymore, this property will always be an empty string and will be removed in a future version
*/
public nick: string;

/**
* The chat room's join lock
* @deprecated since chat is not done over xmpp anymore, this is not used anymore
* @deprecated since chat is not done over xmpp anymore, this is not used anymore and will be removed in a future version
*/
public joinLock: AsyncLock;

@@ -41,15 +45,10 @@ class PartyChat extends Base {

/**
* Whether the client is connected to the party chat
* @deprecated since chat is not done over xmpp anymore, this property will always be true
* @deprecated since chat is not done over xmpp anymore, this property will always be true and will be removed in a future version
*/
public isConnected: boolean;

/**
* Holds the account ids, which will not receive party messages anymore from the currently logged in user
*/
public bannedAccountIds: Set<string>;

/**
* @param client The main client
* @param party The chat room's party
@@ -64,21 +63,24 @@ class PartyChat extends Base {
this.isConnected = true;

this.party = party;
this.bannedAccountIds = new Set<string>();
}

/**
* Sends a message to this party chat
* @param content The message that will be sent
* @throws {PartyChatConversationNotFound} When the client is the only party member
*/
public async send(content: string) {
if (this.party.members.size < 2) {
throw new PartyChatConversationNotFound();
}

const messageId = await this.client.chat.sendMessageInConversation(
this.conversationId,
{
body: content,
},
this.party.members
.filter((x) => !this.bannedAccountIds.has(x.id))
.map((x) => x.id),
);

@@ -92,32 +94,36 @@ class PartyChat extends Base {

/**
* Joins this party chat
* @deprecated since chat is not done over xmpp anymore, this function will do nothing
* @deprecated since chat is not done over xmpp anymore, this function will do nothing and will be removed in a future version
*/
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-empty-function
public async join() { }
// eslint-disable-next-line class-methods-use-this
public async join() {
const deprecatedFn = deprecate(() => { }, deprecationNotOverXmppAnymore);

return deprecatedFn();
}

/**
* Leaves this party chat
* @deprecated since chat is not done over xmpp anymore, this function will do nothing
* @deprecated since chat is not done over xmpp anymore, this function will do nothing and will be removed in a future version
*/
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-empty-function
public async leave() { }
// eslint-disable-next-line class-methods-use-this
public async leave() {
const deprecatedFn = deprecate(() => { }, deprecationNotOverXmppAnymore);

/**
* Ban a member from receiving party messages from the logged in user
* @param member The member that should be banned
*/
public async ban(member: string) {
this.bannedAccountIds.add(member);
return deprecatedFn();
}

/**
* Unban a member from receiving party messages from the logged in user
* @param member The member that should be unbanned
*/
public async unban(member: string) {
this.bannedAccountIds.delete(member);
* Ban a member from this party chat
* @param member The member that should be banned
* @deprecated since chat is not done over xmpp anymore, this function will do nothing and will be removed in a future version
*/
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars
public async ban(member: string) {
const deprecatedFn = deprecate(() => { }, deprecationNotOverXmppAnymore);

return deprecatedFn();
}
}

38 changes: 26 additions & 12 deletions src/xmpp/XMPP.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/* eslint-disable max-len */
import { createClient as createStanzaClient } from 'stanza';
import crypto from 'crypto';
import { deprecate } from 'util';
import Base from '../Base';
import Endpoints from '../../resources/Endpoints';
import PartyMessage from '../structures/party/PartyMessage';
@@ -27,6 +29,8 @@ import XMPPConnectionError from '../exceptions/XMPPConnectionError';
import type { Stanzas, Agent, Constants } from 'stanza';
import type Client from '../Client';

const deprecationNotOverXmppAnymore = 'Chatting is not done over XMPP anymore, this function will be removed in a future version';

/**
* Represents the client's XMPP manager
* @private
@@ -671,24 +675,26 @@ class XMPP extends Base {
* @param to The message receiver's JID
* @param content The message that will be sent
* @param type The message type (eg "chat" or "groupchat")
* @deprecated this is useless now, since chat messages are handeled via an rest api now see {@link Client#chat}
* @deprecated this doesn't work anymore, since chat messages are handled via an rest api now see {@link Client#chat}. This function will be removed in a future version
*/
public async sendMessage(to: string, content: string, type: Constants.MessageType = 'chat') {
return this.waitForSentMessage(this.connection!.sendMessage({
const deprecatedFn = deprecate(async () => this.waitForSentMessage(this.connection!.sendMessage({
to,
body: content,
type,
}));
})), deprecationNotOverXmppAnymore);

return deprecatedFn();
}

/**
* Wait until a message is sent
* @param id The message id
* @param timeout How long to wait for the message
* @deprecated this is useless now, since chat messages are handeled via an rest api now see {@link Client#chat}
* @deprecated this doesn't work anymore, since chat messages are handled via an rest api now see {@link Client#chat}. This function will be removed in a future version
*/
public waitForSentMessage(id: string, timeout = 1000) {
return new Promise<Stanzas.Message | undefined>((res) => {
const deprecatedFn = deprecate(async () => new Promise<Stanzas.Message | undefined>((res) => {
// eslint-disable-next-line no-undef
let messageTimeout: NodeJS.Timeout;

@@ -705,36 +711,44 @@ class XMPP extends Base {
res(undefined);
this.connection!.removeListener('message:sent', listener);
}, timeout);
});
}), deprecationNotOverXmppAnymore);

return deprecatedFn();
}

/**
* Joins a multi user chat room (MUC)
* @param jid The room's JID
* @param nick The client's nickname
* @deprecated this is useless now, since chat messages are handeled via an rest api now see {@link Client#chat}
* @deprecated this doesn't work anymore, since chat messages are handled via an rest api now see {@link Client#chat}. This function will be removed in a future version
*/
public async joinMUC(jid: string, nick: string) {
return this.connection!.joinRoom(jid, nick);
const deprecatedFn = deprecate(async () => this.connection!.joinRoom(jid, nick), deprecationNotOverXmppAnymore);

return deprecatedFn();
}

/**
* Leaves a multi user chat room (MUC)
* @param jid The room's JID
* @param nick The client's nickname
* @deprecated this is useless now, since chat messages are handeled via an rest api now see {@link Client#chat}
* @deprecated this doesn't work anymore, since chat messages are handled via an rest api now see {@link Client#chat}. This function will be removed in a future version
*/
public async leaveMUC(jid: string, nick: string) {
return this.connection!.leaveRoom(jid, nick);
const deprecatedFn = deprecate(async () => this.connection!.leaveRoom(jid, nick), deprecationNotOverXmppAnymore);

return deprecatedFn();
}

/**
* Bans a member from a multi user chat room
* @param member The member that should be banned
* @deprecated this is useless now, since chat messages are handeled via an rest api now see {@link Client#chat}
* @deprecated this doesn't work anymore, since chat messages are handled via an rest api now see {@link Client#chat}. This function will be removed in a future version
*/
public async ban(jid: string, member: string) {
return this.connection!.ban(jid, `${member}@${Endpoints.EPIC_PROD_ENV}`);
const deprecatedFn = deprecate(async () => this.connection!.ban(jid, `${member}@${Endpoints.EPIC_PROD_ENV}`), deprecationNotOverXmppAnymore);

return deprecatedFn();
}
}