BetterDiscordApp-v2/client/src/structs/discord/channel.js

442 lines
14 KiB
JavaScript

/*
* BetterDiscord Channel Struct
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { DiscordApi, DiscordApiModules as Modules } from 'modules';
import { List, InsufficientPermissions } from 'structs';
import { Guild } from './guild';
import { Message } from './message';
import { User, GuildMember } from './user';
const channels = new WeakMap();
/**
* Class representing a Discord Channel
*/
export class Channel {
constructor(data) {
if (channels.has(data)) return channels.get(data);
channels.set(data, this);
this.discordObject = data;
}
static from(channel) {
switch (channel.type) {
default: return new Channel(channel);
case 0: return new GuildTextChannel(channel);
case 1: return new DirectMessageChannel(channel);
case 2: return new GuildVoiceChannel(channel);
case 3: return new GroupChannel(channel);
case 4: return new ChannelCategory(channel);
}
}
static fromId(id) {
const channel = Modules.ChannelStore.getChannel(id);
if (channel) return Channel.from(channel);
}
static get GuildChannel() { return GuildChannel }
static get GuildTextChannel() { return GuildTextChannel }
static get GuildVoiceChannel() { return GuildVoiceChannel }
static get ChannelCategory() { return ChannelCategory }
static get PrivateChannel() { return PrivateChannel }
static get DirectMessageChannel() { return DirectMessageChannel }
static get GroupChannel() { return GroupChannel }
get id() { return this.discordObject.id }
get applicationId() { return this.discordObject.application_id }
get type() { return this.discordObject.type }
get name() { return this.discordObject.name }
/**
* Send a message in this channel.
* @param {String} content The new message's content
* @param {Boolean} parse Whether to parse the message or send it as it is
* @return {Promise => Message}
*/
async sendMessage(content, parse = false) {
if (this.assertPermissions) this.assertPermissions('SEND_MESSAGES', Modules.DiscordPermissions.VIEW_CHANNEL | Modules.DiscordPermissions.SEND_MESSAGES);
this.select();
if (parse) content = Modules.MessageParser.parse(this.discordObject, content);
else content = {content};
const response = await Modules.MessageActions._sendMessage(this.id, content);
return Message.from(Modules.MessageStore.getMessage(this.id, response.body.id));
}
/**
* Send a bot message in this channel that only the current user can see.
* @param {String} content The new message's content
* @return {Message}
*/
sendBotMessage(content) {
this.select();
const message = Modules.MessageParser.createBotMessage(this.id, content);
Modules.MessageActions.receiveMessage(this.id, message);
return Message.from(Modules.MessageStore.getMessage(this.id, message.id));
}
/**
* A list of messages in this channel.
* @type {List<Message>}
*/
get messages() {
const messages = Modules.MessageStore.getMessages(this.id).toArray();
return List.from(messages, m => Message.from(m));
}
/**
* Jumps to the latest message in this channel.
*/
jumpToPresent() {
if (this.assertPermissions) this.assertPermissions('VIEW_CHANNEL', Modules.DiscordPermissions.VIEW_CHANNEL);
if (this.hasMoreAfter) Modules.MessageActions.jumpToPresent(this.id, Modules.DiscordConstants.MAX_MESSAGES_PER_CHANNEL);
else this.messages[this.messages.length - 1].jumpTo(false);
}
get hasMoreAfter() {
return Modules.MessageStore.getMessages(this.id).hasMoreAfter;
}
/**
* Sends an invite in this channel.
* @param {String} code The invite code
* @return {Promise => Messaage}
*/
async sendInvite(code) {
if (this.assertPermissions) this.assertPermissions('SEND_MESSAGES', Modules.DiscordPermissions.VIEW_CHANNEL | Modules.DiscordPermissions.SEND_MESSAGES);
const response = Modules.MessageActions.sendInvite(this.id, code);
return Message.from(Modules.MessageStore.getMessage(this.id, response.body.id));
}
/**
* Opens this channel in the UI.
*/
select() {
if (this.assertPermissions) this.assertPermissions('VIEW_CHANNEL', Modules.DiscordPermissions.VIEW_CHANNEL);
Modules.NavigationUtils.transitionToGuild(this.guildId ? this.guildId : Modules.DiscordConstants.ME, this.id);
}
/**
* Whether this channel is currently selected.
* @type {Boolean}
*/
get isSelected() {
return DiscordApi.currentChannel === this;
}
/**
* Updates this channel.
* @return {Promise}
*/
async updateChannel(body) {
if (this.assertPermissions) this.assertPermissions('MANAGE_CHANNELS', Modules.DiscordPermissions.MANAGE_CHANNELS);
const response = await Modules.APIModule.patch({
url: `${Modules.DiscordConstants.Endpoints.CHANNELS}/${this.id}`,
body
});
this.discordObject = Modules.ChannelStore.getChannel(this.id);
channels.set(this.discordObject, this);
}
}
export class PermissionOverwrite {
constructor(data, channel_id) {
this.discordObject = data;
this.channelId = channel_id;
}
static from(data, channel_id) {
switch (data.type) {
default: return new PermissionOverwrite(data, channel_id);
case 'role': return new RolePermissionOverwrite(data, channel_id);
case 'member': return new MemberPermissionOverwrite(data, channel_id);
}
}
static get RolePermissionOverwrite() { return RolePermissionOverwrite }
static get MemberPermissionOverwrite() { return MemberPermissionOverwrite }
get type() { return this.discordObject.type }
get allow() { return this.discordObject.allow }
get deny() { return this.discordObject.deny }
get channel() {
return Channel.fromId(this.channelId);
}
get guild() {
return this.channel ? this.channel.guild : null;
}
}
export class RolePermissionOverwrite extends PermissionOverwrite {
get roleId() { return this.discordObject.id }
get role() {
return this.guild ? this.guild.roles.find(r => r.id === this.roleId) : null;
}
}
export class MemberPermissionOverwrite extends PermissionOverwrite {
get memberId() { return this.discordObject.id }
get member() {
return GuildMember.fromId(this.memberId);
}
}
export class GuildChannel extends Channel {
static get PermissionOverwrite() { return PermissionOverwrite }
get guildId() { return this.discordObject.guild_id }
get parentId() { return this.discordObject.parent_id } // Channel category
get position() { return this.discordObject.position }
get nicks() { return this.discordObject.nicks }
checkPermissions(perms) {
return Modules.PermissionUtils.can(perms, DiscordApi.currentUser, this.discordObject);
}
assertPermissions(name, perms) {
if (!this.checkPermissions(perms)) throw new InsufficientPermissions(name);
}
get category() {
return Channel.fromId(this.parentId);
}
/**
* The current user's permissions on this channel.
*/
get permissions() {
return Modules.GuildPermissions.getChannelPermissions(this.id);
}
get permissionOverwrites() {
return List.from(Object.entries(this.discordObject.permissionOverwrites), ([i, p]) => PermissionOverwrite.from(p, this.id));
}
get guild() {
return Guild.fromId(this.guildId);
}
/**
* Whether this channel is the guild's default channel.
*/
get isDefaultChannel() {
return Modules.GuildChannelsStore.getDefaultChannel(this.guildId).id === this.id;
}
/**
* Opens this channel's settings window.
* @param {String} section The section to open (see DiscordConstants.ChannelSettingsSections)
*/
openSettings(section = 'OVERVIEW') {
Modules.ChannelSettingsWindow.setSection(section);
Modules.ChannelSettingsWindow.open(this.id);
}
/**
* Updates this channel's name.
* @param {String} name The channel's new name
* @return {Promise}
*/
updateName(name) {
return this.updateChannel({ name });
}
/**
* Changes the channel's position.
* @param {Number} position The channel's new position
* @return {Promise}
*/
changeSortLocation(position = 0) {
if (position instanceof GuildChannel) position = position.position;
return this.updateChannel({ position });
}
/**
* Updates this channel's permission overwrites.
* @param {Array} permissionOverwrites An array of permission overwrites
* @return {Promise}
*/
updatePermissionOverwrites(permission_overwrites) {
return this.updateChannel({ permission_overwrites });
}
/**
* Updates this channel's category.
* @param {ChannelCategory} category The new channel category
* @return {Promise}
*/
updateCategory(category) {
return this.updateChannel({ parent_id: category.id || category });
}
}
// Type 0 - GUILD_TEXT
export class GuildTextChannel extends GuildChannel {
get type() { return 'GUILD_TEXT' }
get topic() { return this.discordObject.topic }
get nsfw() { return this.discordObject.nsfw }
/**
* Updates this channel's topic.
* @param {String} topc The new channel topic
* @return {Promise}
*/
updateTopic(topic) {
return this.updateChannel({ topic });
}
/**
* Updates this channel's not-safe-for-work flag.
* @param {Boolean} nsfw Whether the channel should be marked as NSFW
* @return {Promise}
*/
setNsfw(nsfw = true) {
return this.updateChannel({ nsfw });
}
setNotNsfw() {
return this.setNsfw(false);
}
}
// Type 2 - GUILD_VOICE
export class GuildVoiceChannel extends GuildChannel {
get type() { return 'GUILD_VOICE' }
get userLimit() { return this.discordObject.userLimit }
get bitrate() { return this.discordObject.bitrate }
sendMessage() { throw new Error('Cannot send messages in a voice channel.'); }
get messages() { return new List(); }
jumpToPresent() { throw new Error('Cannot select a voice channel.'); }
get hasMoreAfter() { return false; }
sendInvite() { throw new Error('Cannot invite someone to a voice channel.'); }
select() { throw new Error('Cannot select a voice channel.'); }
/**
* Updates this channel's bitrate.
* @param {Number} bitrate The new bitrate
* @return {Promise}
*/
updateBitrate(bitrate) {
return this.updateChannel({ bitrate });
}
/**
* Updates this channel's user limit.
* @param {Number} userLimit The new user limit
* @return {Promise}
*/
updateUserLimit(user_limit) {
return this.updateChannel({ user_limit });
}
}
// Type 4 - GUILD_CATEGORY
export class ChannelCategory extends GuildChannel {
get type() { return 'GUILD_CATEGORY' }
get parentId() { return undefined }
get category() { return undefined }
sendMessage() { throw new Error('Cannot send messages in a channel category.'); }
get messages() { return new List(); }
jumpToPresent() { throw new Error('Cannot select a channel category.'); }
get hasMoreAfter() { return false; }
sendInvite() { throw new Error('Cannot invite someone to a channel category.'); }
select() { throw new Error('Cannot select a channel category.'); }
updateCategory() { throw new Error('Cannot set a channel category on another channel category.'); }
/**
* A list of channels in this category.
*/
get channels() {
return List.from(this.guild.channels.filter(c => c.parentId === this.id));
}
/**
* Opens the create channel modal for this guild.
* @param {Number} type The type of channel to create - either 0 (text), 2 (voice) or 4 (category)
* @param {GuildChannel} clone A channel to clone permissions of
*/
openCreateChannelModal(type, category, clone) {
this.guild.openCreateChannelModal(type, this.id, this, clone);
}
/**
* Creates a channel in this category.
* @param {Number} type The type of channel to create - either 0 (text) or 2 (voice)
* @param {String} name A name for the new channel
* @param {Array} permission_overwrites An array of PermissionOverwrite-like objects - leave to use the permissions of the category
* @return {Promise => GuildChannel}
*/
createChannel(type, name, permission_overwrites) {
return this.guild.createChannel(type, name, this, permission_overwrites);
}
}
export class PrivateChannel extends Channel {
get userLimit() { return this.discordObject.userLimit }
get bitrate() { return this.discordObject.bitrate }
}
// Type 1 - DM
export class DirectMessageChannel extends PrivateChannel {
get type() { return 'DM' }
get recipientId() { return this.discordObject.recipients[0] }
/**
* The other user of this direct message channel.
*/
get recipient() {
return User.fromId(this.recipientId);
}
}
// Type 3 - GROUP_DM
export class GroupChannel extends PrivateChannel {
get ownerId() { return this.discordObject.ownerId }
get type() { return 'GROUP_DM' }
get name() { return this.discordObject.name }
get icon() { return this.discordObject.icon }
/**
* A list of the other members of this group direct message channel.
*/
get members() {
return List.from(this.discordObject.recipients, id => User.fromId(id));
}
/**
* The owner of this group direct message channel. This is usually the person who created it.
*/
get owner() {
return User.fromId(this.ownerId);
}
/**
* Updates this channel's name.
* @param {String} name The channel's new name
* @return {Promise}
*/
updateName(name) {
return this.updateChannel({ name });
}
}