2022-04-12 05:46:08 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const { Collection } = require('@discordjs/collection');
|
2024-01-08 13:41:56 +00:00
|
|
|
const BaseManager = require('./BaseManager');
|
2022-04-12 05:46:08 +00:00
|
|
|
const { GuildMember } = require('../structures/GuildMember');
|
|
|
|
const { Message } = require('../structures/Message');
|
|
|
|
const ThreadMember = require('../structures/ThreadMember');
|
|
|
|
const User = require('../structures/User');
|
|
|
|
const { RelationshipTypes } = require('../util/Constants');
|
|
|
|
|
|
|
|
/**
|
2022-04-26 05:41:31 +00:00
|
|
|
* Manages API methods for Relationships and stores their cache.
|
2022-04-12 05:46:08 +00:00
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
class RelationshipManager extends BaseManager {
|
2022-04-16 10:44:43 +00:00
|
|
|
constructor(client, users) {
|
2024-01-08 13:41:56 +00:00
|
|
|
super(client);
|
2022-06-11 13:13:52 +00:00
|
|
|
/**
|
2022-06-11 13:17:27 +00:00
|
|
|
* A collection of users this manager is caching. (Type: Number)
|
2022-06-11 13:13:52 +00:00
|
|
|
* @type {Collection<Snowflake, RelationshipTypes>}
|
|
|
|
*/
|
2022-04-16 10:44:43 +00:00
|
|
|
this.cache = new Collection();
|
2024-01-08 13:41:56 +00:00
|
|
|
/**
|
|
|
|
* @type {Collection<Snowflake, string>}
|
|
|
|
*/
|
|
|
|
this.friendNicknames = new Collection();
|
|
|
|
/**
|
|
|
|
* @type {Collection<Snowflake, Date>}
|
|
|
|
*/
|
|
|
|
this.sinceCache = new Collection();
|
2022-04-16 10:44:43 +00:00
|
|
|
this._setup(users);
|
|
|
|
}
|
2022-04-12 05:46:08 +00:00
|
|
|
|
2022-09-09 16:44:45 +00:00
|
|
|
/**
|
|
|
|
* Get all friends
|
|
|
|
* @type {Collection<Snowflake, User>}
|
|
|
|
* @readonly
|
|
|
|
*/
|
|
|
|
get friendCache() {
|
|
|
|
const users = this.cache
|
|
|
|
.filter(value => value === RelationshipTypes.FRIEND)
|
2024-01-08 13:41:56 +00:00
|
|
|
.map((_, key) => [key, this.client.users.cache.get(key)]);
|
2022-09-09 16:44:45 +00:00
|
|
|
return new Collection(users);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get all blocked users
|
|
|
|
* @type {Collection<Snowflake, User>}
|
|
|
|
* @readonly
|
|
|
|
*/
|
|
|
|
get blockedCache() {
|
|
|
|
const users = this.cache
|
|
|
|
.filter(value => value === RelationshipTypes.BLOCKED)
|
2024-01-08 13:41:56 +00:00
|
|
|
.map((_, key) => [key, this.client.users.cache.get(key)]);
|
2022-09-09 16:44:45 +00:00
|
|
|
return new Collection(users);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get all incoming friend requests
|
|
|
|
* @type {Collection<Snowflake, User>}
|
|
|
|
* @readonly
|
|
|
|
*/
|
|
|
|
get incomingCache() {
|
|
|
|
const users = this.cache
|
|
|
|
.filter(value => value === RelationshipTypes.PENDING_INCOMING)
|
2024-01-08 13:41:56 +00:00
|
|
|
.map((_, key) => [key, this.client.users.cache.get(key)]);
|
2022-09-09 16:44:45 +00:00
|
|
|
return new Collection(users);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get all outgoing friend requests
|
|
|
|
* @type {Collection<Snowflake, User>}
|
|
|
|
* @readonly
|
|
|
|
*/
|
|
|
|
get outgoingCache() {
|
|
|
|
const users = this.cache
|
|
|
|
.filter(value => value === RelationshipTypes.PENDING_OUTGOING)
|
2024-01-08 13:41:56 +00:00
|
|
|
.map((_, key) => [key, this.client.users.cache.get(key)]);
|
2022-09-09 16:44:45 +00:00
|
|
|
return new Collection(users);
|
|
|
|
}
|
|
|
|
|
2024-01-08 13:41:56 +00:00
|
|
|
/**
|
|
|
|
* @typedef {Object} RelationshipJSONData
|
|
|
|
* @property {Snowflake} id The ID of the target user
|
|
|
|
* @property {RelationshipTypes} type The type of relationship
|
|
|
|
* @property {string | null} nickname The nickname of the user in this relationship (1-32 characters)
|
|
|
|
* @property {string} since When the user requested a relationship (ISO8601 timestamp)
|
|
|
|
*/
|
|
|
|
|
2022-06-12 12:13:12 +00:00
|
|
|
/**
|
|
|
|
* Return array of cache
|
2024-01-08 13:41:56 +00:00
|
|
|
* @returns {RelationshipJSONData[]}
|
2022-06-12 12:13:12 +00:00
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
toJSON() {
|
|
|
|
return this.cache.map((value, key) => ({
|
|
|
|
id: key,
|
|
|
|
type: RelationshipTypes[value],
|
|
|
|
nickname: this.friendNicknames.get(key),
|
|
|
|
since: this.sinceCache.get(key).toISOString(),
|
|
|
|
}));
|
2022-06-12 12:13:12 +00:00
|
|
|
}
|
|
|
|
|
2022-04-26 05:41:31 +00:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
* @param {Array<User>} users An array of users to add to the cache
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2022-04-16 10:44:43 +00:00
|
|
|
_setup(users) {
|
|
|
|
if (!Array.isArray(users)) return;
|
|
|
|
for (const relationShip of users) {
|
2024-01-08 13:41:56 +00:00
|
|
|
this.friendNicknames.set(relationShip.id, relationShip.nickname);
|
2022-04-16 10:44:43 +00:00
|
|
|
this.cache.set(relationShip.id, relationShip.type);
|
2024-01-08 13:41:56 +00:00
|
|
|
this.sinceCache.set(relationShip.id, new Date(relationShip.since || 0));
|
2022-04-12 05:46:08 +00:00
|
|
|
}
|
2022-04-16 10:44:43 +00:00
|
|
|
}
|
2022-04-12 05:46:08 +00:00
|
|
|
|
2022-04-16 10:44:43 +00:00
|
|
|
/**
|
|
|
|
* Resolves a {@link UserResolvable} to a {@link User} id.
|
|
|
|
* @param {UserResolvable} user The UserResolvable to identify
|
|
|
|
* @returns {?Snowflake}
|
|
|
|
*/
|
|
|
|
resolveId(user) {
|
|
|
|
if (user instanceof ThreadMember) return user.id;
|
|
|
|
if (user instanceof GuildMember) return user.user.id;
|
|
|
|
if (user instanceof Message) return user.author.id;
|
|
|
|
if (user instanceof User) return user.id;
|
|
|
|
return user;
|
|
|
|
}
|
2022-04-12 05:46:08 +00:00
|
|
|
|
2022-04-16 10:44:43 +00:00
|
|
|
/**
|
|
|
|
* Obtains a user from Discord, or the user cache if it's already available.
|
2022-10-25 05:37:23 +00:00
|
|
|
* @param {UserResolvable} [user] The user to fetch
|
2022-04-16 10:44:43 +00:00
|
|
|
* @param {BaseFetchOptions} [options] Additional options for this fetch
|
2022-10-25 05:37:23 +00:00
|
|
|
* @returns {Promise<RelationshipTypes|RelationshipManager>}
|
2022-04-16 10:44:43 +00:00
|
|
|
*/
|
2022-04-16 12:01:05 +00:00
|
|
|
async fetch(user, { force = false } = {}) {
|
2022-10-25 05:37:23 +00:00
|
|
|
if (user) {
|
|
|
|
const id = this.resolveId(user);
|
|
|
|
if (!force) {
|
|
|
|
const existing = this.cache.get(id);
|
|
|
|
if (existing && !existing.partial) return existing;
|
|
|
|
}
|
|
|
|
const data = await this.client.api.users['@me'].relationships.get();
|
|
|
|
await this._setup(data);
|
|
|
|
return this.cache.get(id);
|
|
|
|
} else {
|
|
|
|
const data = await this.client.api.users['@me'].relationships.get();
|
|
|
|
await this._setup(data);
|
|
|
|
return this;
|
2022-04-12 05:46:08 +00:00
|
|
|
}
|
2022-04-16 10:44:43 +00:00
|
|
|
}
|
2022-04-12 05:46:08 +00:00
|
|
|
|
2022-06-11 13:13:52 +00:00
|
|
|
/**
|
2024-01-08 13:41:56 +00:00
|
|
|
* Deletes a friend / blocked relationship with a client user or cancels a friend request.
|
2022-07-19 13:27:32 +00:00
|
|
|
* @param {UserResolvable} user Target
|
2022-06-11 13:13:52 +00:00
|
|
|
* @returns {Promise<boolean>}
|
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
async deleteRelationship(user) {
|
2022-04-16 10:44:43 +00:00
|
|
|
const id = this.resolveId(user);
|
2024-01-08 13:41:56 +00:00
|
|
|
if (
|
|
|
|
![RelationshipTypes.FRIEND, RelationshipTypes.BLOCKED, RelationshipTypes.PENDING_OUTGOING].includes(
|
|
|
|
this.cache.get(id),
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
return Promise.resolve(false);
|
|
|
|
}
|
|
|
|
await this.client.api.users['@me'].relationships[id].delete({
|
|
|
|
DiscordContext: { location: 'Friends' },
|
|
|
|
});
|
|
|
|
return true;
|
2022-04-16 10:44:43 +00:00
|
|
|
}
|
2022-04-12 05:46:08 +00:00
|
|
|
|
2022-06-11 13:13:52 +00:00
|
|
|
/**
|
2024-01-08 13:41:56 +00:00
|
|
|
* @typedef {Object} FriendRequestOptions
|
|
|
|
* @property {UserResolvable} [user] Target
|
|
|
|
* @property {string} [username] Discord username
|
|
|
|
* @property {number | null} [discriminator] Discord discriminator
|
2022-06-11 13:13:52 +00:00
|
|
|
*/
|
2022-04-12 05:46:08 +00:00
|
|
|
|
2022-06-11 13:13:52 +00:00
|
|
|
/**
|
|
|
|
* Sends a friend request.
|
2024-01-08 13:41:56 +00:00
|
|
|
* @param {FriendRequestOptions} options Target
|
2022-06-11 13:13:52 +00:00
|
|
|
* @returns {Promise<boolean>}
|
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
async sendFriendRequest(options) {
|
|
|
|
if (options?.user) {
|
|
|
|
const id = this.resolveId(options.user);
|
|
|
|
await this.client.api.users['@me'].relationships[id].put({
|
|
|
|
data: {},
|
|
|
|
DiscordContext: { location: 'ContextMenu' },
|
|
|
|
});
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
await this.client.api.users['@me'].relationships.post({
|
|
|
|
data: {
|
|
|
|
username: options.username,
|
|
|
|
discriminator: options.discriminator,
|
|
|
|
},
|
|
|
|
DiscordContext: { location: 'Add Friend' },
|
|
|
|
});
|
|
|
|
return true;
|
|
|
|
}
|
2022-07-19 10:50:41 +00:00
|
|
|
}
|
|
|
|
|
2022-06-11 13:13:52 +00:00
|
|
|
/**
|
|
|
|
* Accepts a friend request.
|
|
|
|
* @param {UserResolvable} user The user to add as a friend
|
|
|
|
* @returns {Promise<boolean>}
|
|
|
|
*/
|
2022-04-16 10:44:43 +00:00
|
|
|
async addFriend(user) {
|
|
|
|
const id = this.resolveId(user);
|
2022-04-16 12:01:05 +00:00
|
|
|
// Check if already friends
|
2024-01-08 13:41:56 +00:00
|
|
|
if (this.cache.get(id) === RelationshipTypes.FRIEND) return Promise.resolve(false);
|
2022-04-16 12:01:05 +00:00
|
|
|
// Check if outgoing request
|
2024-01-08 13:41:56 +00:00
|
|
|
if (this.cache.get(id) === RelationshipTypes.PENDING_OUTGOING) return Promise.resolve(false);
|
2022-04-16 10:44:43 +00:00
|
|
|
await this.client.api.users['@me'].relationships[id].put({
|
|
|
|
data: {
|
|
|
|
type: RelationshipTypes.FRIEND,
|
|
|
|
},
|
2024-01-08 13:41:56 +00:00
|
|
|
DiscordContext: { location: 'Friends' },
|
2022-04-16 10:44:43 +00:00
|
|
|
});
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-09-16 12:08:15 +00:00
|
|
|
/**
|
|
|
|
* Changes the nickname of a friend.
|
|
|
|
* @param {UserResolvable} user The user to change the nickname
|
|
|
|
* @param {?string} nickname New nickname
|
|
|
|
* @returns {Promise<boolean>}
|
|
|
|
*/
|
2024-01-08 13:41:56 +00:00
|
|
|
async setNickname(user, nickname = null) {
|
2022-09-16 12:08:15 +00:00
|
|
|
const id = this.resolveId(user);
|
2024-01-08 13:41:56 +00:00
|
|
|
if (this.cache.get(id) !== RelationshipTypes.FRIEND) return Promise.resolve(false);
|
2022-09-16 12:08:15 +00:00
|
|
|
await this.client.api.users['@me'].relationships[id].patch({
|
|
|
|
data: {
|
|
|
|
nickname: typeof nickname === 'string' ? nickname : null,
|
|
|
|
},
|
|
|
|
});
|
2024-01-08 13:41:56 +00:00
|
|
|
if (nickname) {
|
|
|
|
this.friendNicknames.set(id, nickname);
|
|
|
|
} else {
|
|
|
|
this.friendNicknames.delete(id);
|
|
|
|
}
|
2022-09-16 12:08:15 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-06-11 13:13:52 +00:00
|
|
|
/**
|
|
|
|
* Blocks a user.
|
|
|
|
* @param {UserResolvable} user User to block
|
|
|
|
* @returns {Promise<boolean>}
|
|
|
|
*/
|
2022-04-16 10:44:43 +00:00
|
|
|
async addBlocked(user) {
|
|
|
|
const id = this.resolveId(user);
|
2022-04-16 12:01:05 +00:00
|
|
|
// Check
|
2024-01-08 13:41:56 +00:00
|
|
|
if (this.cache.get(id) === RelationshipTypes.BLOCKED) return Promise.resolve(false);
|
2022-04-16 10:44:43 +00:00
|
|
|
await this.client.api.users['@me'].relationships[id].put({
|
|
|
|
data: {
|
|
|
|
type: RelationshipTypes.BLOCKED,
|
|
|
|
},
|
2024-01-08 13:41:56 +00:00
|
|
|
DiscordContext: { location: 'ContextMenu' },
|
2022-04-16 10:44:43 +00:00
|
|
|
});
|
|
|
|
return true;
|
|
|
|
}
|
2022-04-12 05:46:08 +00:00
|
|
|
}
|
|
|
|
|
2022-09-09 16:44:45 +00:00
|
|
|
module.exports = RelationshipManager;
|