discord.js-selfbot-v13
Version:
A unofficial discord.js fork for creating selfbots [Based on discord.js v13]
279 lines (260 loc) • 8.65 kB
JavaScript
'use strict';
const { Collection } = require('@discordjs/collection');
const BaseManager = require('./BaseManager');
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');
/**
* Manages API methods for Relationships and stores their cache.
*/
class RelationshipManager extends BaseManager {
constructor(client, users) {
super(client);
/**
* A collection of users this manager is caching. (Type: Number)
* @type {Collection<Snowflake, RelationshipType>}
*/
this.cache = new Collection();
/**
* @type {Collection<Snowflake, string>}
*/
this.friendNicknames = new Collection();
/**
* @type {Collection<Snowflake, Date>}
*/
this.sinceCache = new Collection();
this._setup(users);
}
/**
* Get all friends
* @type {Collection<Snowflake, User>}
* @readonly
*/
get friendCache() {
const users = this.cache
.filter(value => value === RelationshipTypes.FRIEND)
.map((_, key) => [key, this.client.users.cache.get(key)]);
return new Collection(users);
}
/**
* Get all blocked users
* @type {Collection<Snowflake, User>}
* @readonly
*/
get blockedCache() {
const users = this.cache
.filter(value => value === RelationshipTypes.BLOCKED)
.map((_, key) => [key, this.client.users.cache.get(key)]);
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)
.map((_, key) => [key, this.client.users.cache.get(key)]);
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)
.map((_, key) => [key, this.client.users.cache.get(key)]);
return new Collection(users);
}
/**
* @typedef {Object} RelationshipJSONData
* @property {Snowflake} id The ID of the target user
* @property {RelationshipType} 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)
*/
/**
* Return array of cache
* @returns {RelationshipJSONData[]}
*/
toJSON() {
return this.cache.map((value, key) => ({
id: key,
type: RelationshipTypes[value],
nickname: this.friendNicknames.get(key),
since: this.sinceCache.get(key).toISOString(),
}));
}
/**
* @private
* @param {Array<User>} users An array of users to add to the cache
* @returns {void}
*/
_setup(users) {
if (!Array.isArray(users)) return;
for (const relationShip of users) {
this.friendNicknames.set(relationShip.id, relationShip.nickname);
this.cache.set(relationShip.id, relationShip.type);
this.sinceCache.set(relationShip.id, new Date(relationShip.since || 0));
}
}
/**
* 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.match(/\d{17,19}/)?.[0] || null;
}
/**
* Resolves a {@link UserResolvable} to a {@link User} username.
* @param {UserResolvable} user The UserResolvable to identify
* @returns {?string}
*/
resolveUsername(user) {
if (user instanceof ThreadMember) return user.member.user.username;
if (user instanceof GuildMember) return user.user.username;
if (user instanceof Message) return user.author.username;
if (user instanceof User) return user.username;
return user;
}
/**
* Obtains a user from Discord, or the user cache if it's already available.
* @param {UserResolvable} [user] The user to fetch
* @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<RelationshipType|RelationshipManager>}
*/
async fetch(user, { force = false } = {}) {
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;
}
}
/**
* Deletes a friend / blocked relationship with a client user or cancels a friend request.
* @param {UserResolvable} user Target
* @returns {Promise<boolean>}
*/
async deleteRelationship(user) {
throw new Error('Risky action, not finished yet.');
// eslint-disable-next-line no-unreachable
const id = this.resolveId(user);
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: 'ContextMenu' },
});
return true;
}
/**
* Sends a friend request.
* @param {UserResolvable} options Target (User Object, Username, User Id)
* @returns {Promise<boolean>}
*/
async sendFriendRequest(options) {
throw new Error('Risky action, not finished yet.');
// eslint-disable-next-line no-unreachable
const id = this.resolveId(options);
if (id) {
await this.client.api.users['@me'].relationships[id].put({
data: {},
DiscordContext: { location: 'ContextMenu' },
});
} else {
const username = this.resolveUsername(options);
await this.client.api.users['@me'].relationships.post({
versioned: true,
data: {
username,
discriminator: null,
},
DiscordContext: { location: 'Add Friend' },
});
}
return true;
}
/**
* Accepts a friend request.
* @param {UserResolvable} user The user to add as a friend
* @returns {Promise<boolean>}
*/
async addFriend(user) {
throw new Error('Risky action, not finished yet.');
// eslint-disable-next-line no-unreachable
const id = this.resolveId(user);
// Check if already friends
if (this.cache.get(id) === RelationshipTypes.FRIEND) return Promise.resolve(false);
// Check if outgoing request
if (this.cache.get(id) === RelationshipTypes.PENDING_OUTGOING) return Promise.resolve(false);
await this.client.api.users['@me'].relationships[id].put({
data: { confirm_stranger_request: true },
DiscordContext: { location: 'Friends' },
});
return true;
}
/**
* Changes the nickname of a friend.
* @param {UserResolvable} user The user to change the nickname
* @param {?string} nickname New nickname
* @returns {Promise<boolean>}
*/
async setNickname(user, nickname = null) {
const id = this.resolveId(user);
if (this.cache.get(id) !== RelationshipTypes.FRIEND) return Promise.resolve(false);
await this.client.api.users['@me'].relationships[id].patch({
data: {
nickname: typeof nickname === 'string' ? nickname : null,
},
});
if (nickname) {
this.friendNicknames.set(id, nickname);
} else {
this.friendNicknames.delete(id);
}
return true;
}
/**
* Blocks a user.
* @param {UserResolvable} user User to block
* @returns {Promise<boolean>}
*/
async addBlocked(user) {
throw new Error('Risky action, not finished yet.');
// eslint-disable-next-line no-unreachable
const id = this.resolveId(user);
// Check
if (this.cache.get(id) === RelationshipTypes.BLOCKED) return Promise.resolve(false);
await this.client.api.users['@me'].relationships[id].put({
data: {
type: RelationshipTypes.BLOCKED,
},
DiscordContext: { location: 'ContextMenu' },
});
return true;
}
}
module.exports = RelationshipManager;