mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-15 02:53:31 +01:00
add channel categories (#1727)
* add channel categories * add specific class * speed * Update Channel.js * fix type typo * Update Channel.js * rewrite position stuff in prep for category sorting * fix small issues in generation of permissions * Update Guild.js * Update Constants.js * Update GuildChannel.js * doc fix * Update GuildChannel.js * <.<
This commit is contained in:
9
src/structures/CategoryChannel.js
Normal file
9
src/structures/CategoryChannel.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const GuildChannel = require('./GuildChannel');
|
||||||
|
|
||||||
|
class CategoryChannel extends GuildChannel {
|
||||||
|
get children() {
|
||||||
|
return this.guild.channels.filter(c => c.parentID === this.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CategoryChannel;
|
||||||
@@ -17,6 +17,7 @@ class Channel extends Base {
|
|||||||
* * `group` - a Group DM channel
|
* * `group` - a Group DM channel
|
||||||
* * `text` - a guild text channel
|
* * `text` - a guild text channel
|
||||||
* * `voice` - a guild voice channel
|
* * `voice` - a guild voice channel
|
||||||
|
* * `category` - a guild category channel
|
||||||
* * `unknown` - a generic channel of unknown type, could be Channel or GuildChannel
|
* * `unknown` - a generic channel of unknown type, could be Channel or GuildChannel
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
@@ -69,6 +70,7 @@ class Channel extends Base {
|
|||||||
const GroupDMChannel = require('./GroupDMChannel');
|
const GroupDMChannel = require('./GroupDMChannel');
|
||||||
const TextChannel = require('./TextChannel');
|
const TextChannel = require('./TextChannel');
|
||||||
const VoiceChannel = require('./VoiceChannel');
|
const VoiceChannel = require('./VoiceChannel');
|
||||||
|
const CategoryChannel = require('./CategoryChannel');
|
||||||
const GuildChannel = require('./GuildChannel');
|
const GuildChannel = require('./GuildChannel');
|
||||||
const types = Constants.ChannelTypes;
|
const types = Constants.ChannelTypes;
|
||||||
let channel;
|
let channel;
|
||||||
@@ -86,6 +88,9 @@ class Channel extends Base {
|
|||||||
case types.VOICE:
|
case types.VOICE:
|
||||||
channel = new VoiceChannel(guild, data);
|
channel = new VoiceChannel(guild, data);
|
||||||
break;
|
break;
|
||||||
|
case types.CATEGORY:
|
||||||
|
channel = new CategoryChannel(guild, data);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
channel = new GuildChannel(guild, data);
|
channel = new GuildChannel(guild, data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -891,10 +891,9 @@ class Guild extends Base {
|
|||||||
/**
|
/**
|
||||||
* Creates a new channel in the guild.
|
* Creates a new channel in the guild.
|
||||||
* @param {string} name The name of the new channel
|
* @param {string} name The name of the new channel
|
||||||
* @param {string} type The type of the new channel, either `text` or `voice`
|
* @param {string} type The type of the new channel, either `text`, `voice`, or `category`
|
||||||
* @param {Object} [options={}] Options
|
* @param {Object} [options] Options
|
||||||
* @param {Array<PermissionOverwrites|ChannelCreationOverwrites>} [options.overwrites] Permission overwrites
|
* @param {Array<PermissionOverwrites|ChannelCreationOverwrites>} [options.overwrites] Permission overwrites
|
||||||
* to apply to the new channel
|
|
||||||
* @param {string} [options.reason] Reason for creating this channel
|
* @param {string} [options.reason] Reason for creating this channel
|
||||||
* @returns {Promise<TextChannel|VoiceChannel>}
|
* @returns {Promise<TextChannel|VoiceChannel>}
|
||||||
* @example
|
* @example
|
||||||
@@ -1205,31 +1204,17 @@ class Guild extends Base {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a collection of channels in the current guild sorted by position.
|
* Fetches a collection of channels in the current guild sorted by position.
|
||||||
* @param {string} type The channel type
|
* @param {Channel} channel Channel
|
||||||
* @returns {Collection<Snowflake, GuildChannel>}
|
* @returns {Collection<Snowflake, GuildChannel>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_sortedChannels(type) {
|
_sortedChannels(channel) {
|
||||||
return this._sortPositionWithID(this.channels.filter(c => {
|
const sort = col => col
|
||||||
if (type === 'voice' && c.type === 'voice') return true;
|
.sort((a, b) => a.rawPosition - b.rawPosition || Long.fromString(a.id).sub(Long.fromString(b.id)).toNumber());
|
||||||
else if (type !== 'voice' && c.type !== 'voice') return true;
|
if (channel.type === Constants.ChannelTypes.CATEGORY) {
|
||||||
else return type === c.type;
|
return sort(this.channels.filter(c => c.type === Constants.ChannelTypes.CATEGORY));
|
||||||
}));
|
}
|
||||||
}
|
return sort(this.channels.filter(c => c.parent === channel.parent));
|
||||||
|
|
||||||
/**
|
|
||||||
* Sorts a collection by object position or ID if the positions are equivalent.
|
|
||||||
* Intended to be identical to Discord's sorting method.
|
|
||||||
* @param {Collection} collection The collection to sort
|
|
||||||
* @returns {Collection}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_sortPositionWithID(collection) {
|
|
||||||
return collection.sort((a, b) =>
|
|
||||||
a.position !== b.position ?
|
|
||||||
a.position - b.position :
|
|
||||||
Long.fromString(a.id).sub(Long.fromString(b.id)).toNumber()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ const Channel = require('./Channel');
|
|||||||
const Role = require('./Role');
|
const Role = require('./Role');
|
||||||
const Invite = require('./Invite');
|
const Invite = require('./Invite');
|
||||||
const PermissionOverwrites = require('./PermissionOverwrites');
|
const PermissionOverwrites = require('./PermissionOverwrites');
|
||||||
|
const Util = require('../util/Util');
|
||||||
const Permissions = require('../util/Permissions');
|
const Permissions = require('../util/Permissions');
|
||||||
const Collection = require('../util/Collection');
|
const Collection = require('../util/Collection');
|
||||||
const Constants = require('../util/Constants');
|
const Constants = require('../util/Constants');
|
||||||
@@ -32,10 +33,16 @@ class GuildChannel extends Channel {
|
|||||||
this.name = data.name;
|
this.name = data.name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The position of the channel in the list
|
* The raw position of the channel from discord
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
this.position = data.position;
|
this.rawPosition = data.position;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the category parent of this channel
|
||||||
|
* @type {?Snowflake}
|
||||||
|
*/
|
||||||
|
this.parentID = data.parent_id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of permission overwrites in this channel for roles and users
|
* A map of permission overwrites in this channel for roles and users
|
||||||
@@ -49,13 +56,21 @@ class GuildChannel extends Channel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The category parent of this channel
|
||||||
|
* @type {?GuildChannel}
|
||||||
|
*/
|
||||||
|
get parent() {
|
||||||
|
return this.guild.channels.get(this.parentID);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The position of the channel
|
* The position of the channel
|
||||||
* @type {number}
|
* @type {number}
|
||||||
* @readonly
|
* @readonly
|
||||||
*/
|
*/
|
||||||
get calculatedPosition() {
|
get position() {
|
||||||
const sorted = this.guild._sortedChannels(this.type);
|
const sorted = this.guild._sortedChannels(this);
|
||||||
return sorted.array().indexOf(sorted.get(this.id));
|
return sorted.array().indexOf(sorted.get(this.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,34 +85,32 @@ class GuildChannel extends Channel {
|
|||||||
if (!member) return null;
|
if (!member) return null;
|
||||||
if (member.id === this.guild.ownerID) return new Permissions(Permissions.ALL);
|
if (member.id === this.guild.ownerID) return new Permissions(Permissions.ALL);
|
||||||
|
|
||||||
let permissions = 0;
|
let resolved = this.guild.roles.get(this.guild.id).permissions;
|
||||||
|
|
||||||
const roles = member.roles;
|
|
||||||
for (const role of roles.values()) permissions |= role.permissions;
|
|
||||||
|
|
||||||
const overwrites = this.overwritesFor(member, true, roles);
|
|
||||||
|
|
||||||
|
const overwrites = this.overwritesFor(member, true);
|
||||||
if (overwrites.everyone) {
|
if (overwrites.everyone) {
|
||||||
permissions &= ~overwrites.everyone._denied;
|
resolved &= ~overwrites.everyone._denied;
|
||||||
permissions |= overwrites.everyone._allowed;
|
resolved |= overwrites.everyone._allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
let allow = 0;
|
let allows = 0;
|
||||||
|
let denies = 0;
|
||||||
for (const overwrite of overwrites.roles) {
|
for (const overwrite of overwrites.roles) {
|
||||||
permissions &= ~overwrite._denied;
|
allows |= overwrite._allowed;
|
||||||
allow |= overwrite._allowed;
|
denies |= overwrite._denied;
|
||||||
}
|
}
|
||||||
permissions |= allow;
|
resolved &= ~denies;
|
||||||
|
resolved |= allows;
|
||||||
|
|
||||||
if (overwrites.member) {
|
if (overwrites.member) {
|
||||||
permissions &= ~overwrites.member._denied;
|
resolved &= ~overwrites.member._denied;
|
||||||
permissions |= overwrites.member._allowed;
|
resolved |= overwrites.member._allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
const admin = Boolean(permissions & Permissions.FLAGS.ADMINISTRATOR);
|
const admin = Boolean(resolved & Permissions.FLAGS.ADMINISTRATOR);
|
||||||
if (admin) permissions = Permissions.ALL;
|
if (admin) resolved = Permissions.ALL;
|
||||||
|
|
||||||
return new Permissions(permissions);
|
return new Permissions(resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
overwritesFor(member, verified = false, roles = null) {
|
overwritesFor(member, verified = false, roles = null) {
|
||||||
@@ -236,10 +249,12 @@ class GuildChannel extends Channel {
|
|||||||
return this.client.api.channels(this.id).patch({
|
return this.client.api.channels(this.id).patch({
|
||||||
data: {
|
data: {
|
||||||
name: (data.name || this.name).trim(),
|
name: (data.name || this.name).trim(),
|
||||||
topic: data.topic || this.topic,
|
topic: data.topic,
|
||||||
position: data.position || this.position,
|
position: data.position || this.position,
|
||||||
bitrate: data.bitrate || (this.bitrate ? this.bitrate * 1000 : undefined),
|
bitrate: data.bitrate || (this.bitrate ? this.bitrate * 1000 : undefined),
|
||||||
user_limit: data.userLimit || this.userLimit,
|
user_limit: data.userLimit != null ? data.userLimit : this.userLimit, // eslint-disable-line eqeqeq
|
||||||
|
parent_id: data.parentID,
|
||||||
|
lock_permissions: data.lockPermissions,
|
||||||
},
|
},
|
||||||
reason,
|
reason,
|
||||||
}).then(newData => {
|
}).then(newData => {
|
||||||
@@ -275,8 +290,34 @@ class GuildChannel extends Channel {
|
|||||||
* .then(newChannel => console.log(`Channel's new position is ${newChannel.position}`))
|
* .then(newChannel => console.log(`Channel's new position is ${newChannel.position}`))
|
||||||
* .catch(console.error);
|
* .catch(console.error);
|
||||||
*/
|
*/
|
||||||
setPosition(position, relative) {
|
setPosition(position, { relative, reason }) {
|
||||||
return this.guild.setChannelPosition(this, position, relative).then(() => this);
|
position = Number(position);
|
||||||
|
if (isNaN(position)) return Promise.reject(new TypeError('INVALID_TYPE', 'position', 'number'));
|
||||||
|
let updatedChannels = this.guild._sortedChannels(this).array();
|
||||||
|
Util.moveElementInArray(updatedChannels, this, position, relative);
|
||||||
|
updatedChannels = updatedChannels.map((r, i) => ({ id: r.id, position: i }));
|
||||||
|
return this.client.api.guilds(this.id).channels.patch({ data: updatedChannels, reason })
|
||||||
|
.then(() => {
|
||||||
|
this.client.actions.GuildChannelsPositionUpdate.handle({
|
||||||
|
guild_id: this.id,
|
||||||
|
channels: updatedChannels,
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the category parent of this channel.
|
||||||
|
* @param {GuildChannel|Snowflake} channel Parent channel
|
||||||
|
* @param {boolean} [options.lockPermissions] Lock the permissions to what the parent's permissions are
|
||||||
|
* @param {string} [options.reason] Reason for modifying the parent of this channel
|
||||||
|
* @returns {Promise<GuildChannel>}
|
||||||
|
*/
|
||||||
|
setParent(channel, { lockPermissions = true, reason } = {}) {
|
||||||
|
return this.edit({
|
||||||
|
parentID: channel.id ? channel.id : channel,
|
||||||
|
lockPermissions,
|
||||||
|
}, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -165,13 +165,6 @@ exports.VoiceStatus = {
|
|||||||
DISCONNECTED: 4,
|
DISCONNECTED: 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.ChannelTypes = {
|
|
||||||
TEXT: 0,
|
|
||||||
DM: 1,
|
|
||||||
VOICE: 2,
|
|
||||||
GROUP: 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.OPCodes = {
|
exports.OPCodes = {
|
||||||
DISPATCH: 0,
|
DISPATCH: 0,
|
||||||
HEARTBEAT: 1,
|
HEARTBEAT: 1,
|
||||||
@@ -574,6 +567,14 @@ exports.UserFlags = {
|
|||||||
HYPESQUAD: 1 << 2,
|
HYPESQUAD: 1 << 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.ChannelTypes = {
|
||||||
|
TEXT: 0,
|
||||||
|
DM: 1,
|
||||||
|
VOICE: 2,
|
||||||
|
GROUP: 3,
|
||||||
|
CATEGORY: 4,
|
||||||
|
};
|
||||||
|
|
||||||
exports.ClientApplicationAssetTypes = {
|
exports.ClientApplicationAssetTypes = {
|
||||||
SMALL: 1,
|
SMALL: 1,
|
||||||
BIG: 2,
|
BIG: 2,
|
||||||
|
|||||||
Reference in New Issue
Block a user