Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/structures/CategoryChannel.js
Original file line number Diff line number Diff line change
@@ -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;
5 changes: 5 additions & 0 deletions src/structures/Channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Channel extends Base {
* * `group` - a Group DM channel
* * `text` - a guild text channel
* * `voice` - a guild voice channel
* * `category` - a guild category channel
* * `unknown` - a generic channel of unknown type, could be Channel or GuildChannel
* @type {string}
*/
Expand Down Expand Up @@ -69,6 +70,7 @@ class Channel extends Base {
const GroupDMChannel = require('./GroupDMChannel');
const TextChannel = require('./TextChannel');
const VoiceChannel = require('./VoiceChannel');
const CategoryChannel = require('./CategoryChannel');
const GuildChannel = require('./GuildChannel');
const types = Constants.ChannelTypes;
let channel;
Expand All @@ -86,6 +88,9 @@ class Channel extends Base {
case types.VOICE:
channel = new VoiceChannel(guild, data);
break;
case types.CATEGORY:
channel = new CategoryChannel(guild, data);
break;
default:
channel = new GuildChannel(guild, data);
}
Expand Down
35 changes: 10 additions & 25 deletions src/structures/Guild.js
Original file line number Diff line number Diff line change
Expand Up @@ -890,10 +890,9 @@ class Guild extends Base {
/**
* Creates a new channel in the guild.
* @param {string} name The name of the new channel
* @param {string} type The type of the new channel, either `text` or `voice`
* @param {Object} [options={}] Options
Copy link
Contributor

@PgBiel PgBiel Aug 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be optional?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...it is optional?

Copy link
Contributor

@PgBiel PgBiel Aug 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

he made it not optional in the docs, see the lines after it @appellation

also he just made it optional, and I probably put the wrong line for review ._.

* @param {string} type The type of the new channel, either `text`, `voice`, or `category`
* @param {Object} [options] Options
* @param {Array<PermissionOverwrites|ChannelCreationOverwrites>} [options.overwrites] Permission overwrites
* to apply to the new channel
* @param {string} [options.reason] Reason for creating this channel
* @returns {Promise<TextChannel|VoiceChannel>}
* @example
Expand Down Expand Up @@ -1204,31 +1203,17 @@ class Guild extends Base {

/**
* 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>}
* @private
*/
_sortedChannels(type) {
return this._sortPositionWithID(this.channels.filter(c => {
if (type === 'voice' && c.type === 'voice') return true;
else if (type !== 'voice' && c.type !== 'voice') return true;
else return type === c.type;
}));
}

/**
* 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()
);
_sortedChannels(channel) {
const sort = col => col
.sort((a, b) => a.rawPosition - b.rawPosition || Long.fromString(a.id).sub(Long.fromString(b.id)).toNumber());
if (channel.type === Constants.ChannelTypes.CATEGORY) {
return sort(this.channels.filter(c => c.type === Constants.ChannelTypes.CATEGORY));
}
return sort(this.channels.filter(c => c.parent === channel.parent));
}
}

Expand Down
91 changes: 66 additions & 25 deletions src/structures/GuildChannel.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const Channel = require('./Channel');
const Role = require('./Role');
const Invite = require('./Invite');
const PermissionOverwrites = require('./PermissionOverwrites');
const Util = require('../util/Util');
const Permissions = require('../util/Permissions');
const Collection = require('../util/Collection');
const Constants = require('../util/Constants');
Expand Down Expand Up @@ -32,10 +33,16 @@ class GuildChannel extends Channel {
this.name = data.name;

/**
* The position of the channel in the list
* The raw position of the channel from discord
* @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
Expand All @@ -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
* @type {number}
* @readonly
*/
get calculatedPosition() {
const sorted = this.guild._sortedChannels(this.type);
get position() {
const sorted = this.guild._sortedChannels(this);
return sorted.array().indexOf(sorted.get(this.id));
}

Expand All @@ -70,34 +85,32 @@ class GuildChannel extends Channel {
if (!member) return null;
if (member.id === this.guild.ownerID) return new Permissions(Permissions.ALL);

let permissions = 0;

const roles = member.roles;
for (const role of roles.values()) permissions |= role.permissions;

const overwrites = this.overwritesFor(member, true, roles);
let resolved = this.guild.roles.get(this.guild.id).permissions;

const overwrites = this.overwritesFor(member, true);
if (overwrites.everyone) {
permissions &= ~overwrites.everyone._denied;
permissions |= overwrites.everyone._allowed;
resolved &= ~overwrites.everyone._denied;
resolved |= overwrites.everyone._allowed;
}

let allow = 0;
let allows = 0;
let denies = 0;
for (const overwrite of overwrites.roles) {
permissions &= ~overwrite._denied;
allow |= overwrite._allowed;
allows |= overwrite._allowed;
denies |= overwrite._denied;
}
permissions |= allow;
resolved &= ~denies;
resolved |= allows;

if (overwrites.member) {
permissions &= ~overwrites.member._denied;
permissions |= overwrites.member._allowed;
resolved &= ~overwrites.member._denied;
resolved |= overwrites.member._allowed;
}

const admin = Boolean(permissions & Permissions.FLAGS.ADMINISTRATOR);
if (admin) permissions = Permissions.ALL;
const admin = Boolean(resolved & Permissions.FLAGS.ADMINISTRATOR);
if (admin) resolved = Permissions.ALL;

return new Permissions(permissions);
return new Permissions(resolved);
}

overwritesFor(member, verified = false, roles = null) {
Expand Down Expand Up @@ -236,10 +249,12 @@ class GuildChannel extends Channel {
return this.client.api.channels(this.id).patch({
data: {
name: (data.name || this.name).trim(),
topic: data.topic || this.topic,
topic: data.topic,
position: data.position || this.position,
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,
}).then(newData => {
Expand Down Expand Up @@ -275,8 +290,34 @@ class GuildChannel extends Channel {
* .then(newChannel => console.log(`Channel's new position is ${newChannel.position}`))
* .catch(console.error);
*/
setPosition(position, relative) {
return this.guild.setChannelPosition(this, position, relative).then(() => this);
setPosition(position, { relative, reason }) {
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);
}

/**
Expand Down
15 changes: 8 additions & 7 deletions src/util/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,6 @@ exports.VoiceStatus = {
DISCONNECTED: 4,
};

exports.ChannelTypes = {
TEXT: 0,
DM: 1,
VOICE: 2,
GROUP: 3,
};

exports.OPCodes = {
DISPATCH: 0,
HEARTBEAT: 1,
Expand Down Expand Up @@ -574,6 +567,14 @@ exports.UserFlags = {
HYPESQUAD: 1 << 2,
};

exports.ChannelTypes = {
TEXT: 0,
DM: 1,
VOICE: 2,
GROUP: 3,
CATEGORY: 4,
};

exports.ClientApplicationAssetTypes = {
SMALL: 1,
BIG: 2,
Expand Down