Add "easy to use" Overwrite type, Make GuildChannel(for base), Separate GuildTextChannel from textChannel.ts

This commit is contained in:
Helloyunho 2021-03-20 00:39:14 +09:00
parent b532a99eb4
commit 21ccf4c054
22 changed files with 663 additions and 417 deletions

View File

@ -9,3 +9,4 @@ export type {
} from 'https://deno.land/x/redis@v0.14.1/mod.ts'
export { walk } from 'https://deno.land/std@0.86.0/fs/walk.ts'
export { join } from 'https://deno.land/std@0.86.0/path/mod.ts'
export { Mixin } from 'https://esm.sh/ts-mixer'

View File

@ -4,7 +4,7 @@
"entry": "./mod.ts",
"description": "An easy to use Discord API Library for Deno.",
"homepage": "https://github.com/harmonyland/harmony",
"version": "v1.1.3",
"version": "v1.1.4",
"files": [
"./src/**/*",
"./deps.ts",

13
mod.ts
View File

@ -71,7 +71,12 @@ export {
} from './src/structures/presence.ts'
export { Role } from './src/structures/role.ts'
export { Snowflake } from './src/utils/snowflake.ts'
export { TextChannel, GuildTextChannel } from './src/structures/textChannel.ts'
export { TextChannel } from './src/structures/textChannel.ts'
export {
GuildTextBasedChannel,
GuildTextChannel,
checkGuildTextBasedChannel
} from './src/structures/guildTextChannel.ts'
export type { AllMessageOptions } from './src/structures/textChannel.ts'
export { MessageReaction } from './src/structures/messageReaction.ts'
export { User } from './src/structures/user.ts'
@ -103,7 +108,10 @@ export type {
GuildTextChannelPayload,
GuildVoiceChannelPayload,
GroupDMChannelPayload,
MessageOptions
MessageOptions,
OverwriteAsArg,
Overwrite,
OverwriteAsOptions
} from './src/types/channel.ts'
export type { EmojiPayload } from './src/types/emoji.ts'
export { Verification } from './src/types/guild.ts'
@ -113,6 +121,7 @@ export type {
GuildBanPayload,
GuildFeatures,
GuildChannels,
GuildTextBasedChannels,
GuildCreateOptions,
GuildCreateChannelOptions,
GuildCreateRolePayload

View File

@ -31,7 +31,8 @@ import { webhooksUpdate } from './webhooksUpdate.ts'
import { messageDeleteBulk } from './messageDeleteBulk.ts'
import { userUpdate } from './userUpdate.ts'
import { typingStart } from './typingStart.ts'
import { GuildTextChannel, TextChannel } from '../../structures/textChannel.ts'
import { TextChannel } from '../../structures/textChannel.ts'
import { GuildTextBasedChannel } from '../../structures/guildTextChannel.ts'
import { Guild } from '../../structures/guild.ts'
import { User } from '../../structures/user.ts'
import { Emoji } from '../../structures/emoji.ts'
@ -263,7 +264,7 @@ export type ClientEvents = {
* @param uncached Set of Messages deleted's IDs which were not cached
*/
messageDeleteBulk: [
channel: GuildTextChannel,
channel: GuildTextBasedChannel,
messages: Collection<string, Message>,
uncached: Set<string>
]
@ -356,7 +357,7 @@ export type ClientEvents = {
* @param guild Guild in which Webhooks were updated
* @param channel Channel of which Webhooks were updated
*/
webhooksUpdate: [guild: Guild, channel: GuildTextChannel]
webhooksUpdate: [guild: Guild, channel: GuildTextBasedChannel]
/**
* An Interaction was created
* @param interaction Created interaction object

View File

@ -1,6 +1,6 @@
import { Member } from '../../structures/member.ts'
import { Interaction } from '../../structures/slash.ts'
import { GuildTextChannel } from '../../structures/textChannel.ts'
import { GuildTextBasedChannel } from '../../structures/guildTextChannel.ts'
import { InteractionPayload } from '../../types/slash.ts'
import { Gateway, GatewayEventHandler } from '../index.ts'
@ -17,8 +17,8 @@ export const interactionCreate: GatewayEventHandler = async (
)) as unknown) as Member
const channel =
(await gateway.client.channels.get<GuildTextChannel>(d.channel_id)) ??
(await gateway.client.channels.fetch<GuildTextChannel>(d.channel_id))
(await gateway.client.channels.get<GuildTextBasedChannel>(d.channel_id)) ??
(await gateway.client.channels.fetch<GuildTextBasedChannel>(d.channel_id))
const interaction = new Interaction(gateway.client, d, {
member,

View File

@ -1,5 +1,5 @@
import { Message } from '../../structures/message.ts'
import { GuildTextChannel } from '../../structures/textChannel.ts'
import { GuildTextBasedChannel } from '../../structures/guildTextChannel.ts'
import { MessageDeleteBulkPayload } from '../../types/gateway.ts'
import { Collection } from '../../utils/collection.ts'
import { Gateway, GatewayEventHandler } from '../index.ts'
@ -8,7 +8,7 @@ export const messageDeleteBulk: GatewayEventHandler = async (
gateway: Gateway,
d: MessageDeleteBulkPayload
) => {
let channel = await gateway.client.channels.get<GuildTextChannel>(
let channel = await gateway.client.channels.get<GuildTextBasedChannel>(
d.channel_id
)
// Fetch the channel if not cached
@ -16,7 +16,7 @@ export const messageDeleteBulk: GatewayEventHandler = async (
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
channel = (await gateway.client.channels.fetch(
d.channel_id
)) as GuildTextChannel
)) as GuildTextBasedChannel
const messages = new Collection<string, Message>()
const uncached = new Set<string>()

View File

@ -1,7 +1,7 @@
import { Gateway, GatewayEventHandler } from '../index.ts'
import { Guild } from '../../structures/guild.ts'
import { WebhooksUpdatePayload } from '../../types/gateway.ts'
import { GuildTextChannel } from '../../structures/textChannel.ts'
import { GuildTextBasedChannel } from '../../structures/guildTextChannel.ts'
export const webhooksUpdate: GatewayEventHandler = async (
gateway: Gateway,
@ -10,9 +10,9 @@ export const webhooksUpdate: GatewayEventHandler = async (
const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id)
if (guild === undefined) return
const channel: GuildTextChannel | undefined = (await guild.channels.get(
const channel: GuildTextBasedChannel | undefined = (await guild.channels.get(
d.channel_id
)) as GuildTextChannel
)) as GuildTextBasedChannel
if (channel === undefined)
gateway.client.emit('webhooksUpdateUncached', guild, d.channel_id)
else gateway.client.emit('webhooksUpdate', guild, channel)

View File

@ -5,7 +5,7 @@ import { CategoryChannel } from '../structures/guildCategoryChannel.ts'
import {
ChannelTypes,
GuildChannelPayload,
Overwrite
OverwritePayload
} from '../types/channel.ts'
import { GuildChannels, GuildChannelPayloads } from '../types/guild.ts'
import { CHANNEL, GUILD_CHANNELS } from '../types/endpoint.ts'
@ -20,7 +20,7 @@ export interface CreateChannelOptions {
userLimit?: number
rateLimitPerUser?: number
position?: number
permissionOverwrites?: Overwrite[]
permissionOverwrites?: OverwritePayload[]
parent?: CategoryChannel | string
nsfw?: boolean
}

View File

@ -1,5 +1,5 @@
import { Message } from '../structures/message.ts'
import { GuildTextChannel } from '../structures/textChannel.ts'
import { GuildTextBasedChannel } from '../structures/guildTextChannel.ts'
import { Client, ClientOptions } from './client.ts'
import {
CategoriesManager,
@ -275,7 +275,7 @@ export class CommandClient extends Client implements CommandClientOptions {
if (
command.nsfw === true &&
(msg.guild === undefined ||
((msg.channel as unknown) as GuildTextChannel).nsfw !== true)
((msg.channel as unknown) as GuildTextBasedChannel).nsfw !== true)
)
return this.emit('commandNSFW', ctx)

View File

@ -1,6 +1,22 @@
import { Client } from '../models/client.ts'
import { ChannelPayload, ChannelTypes } from '../types/channel.ts'
import {
ChannelPayload,
ChannelTypes,
ModifyChannelOption,
ModifyChannelPayload,
Overwrite,
OverwritePayload,
OverwriteAsArg,
OverrideType
} from '../types/channel.ts'
import { CHANNEL } from '../types/endpoint.ts'
import { GuildChannelPayloads, GuildChannels } from '../types/guild.ts'
import getChannelByType from '../utils/getChannelByType.ts'
import { Permissions } from '../utils/permissions.ts'
import { SnowflakeBase } from './base.ts'
import { Guild } from './guild.ts'
import { Member } from './member.ts'
import { Role } from './role.ts'
export class Channel extends SnowflakeBase {
type: ChannelTypes
@ -21,3 +37,313 @@ export class Channel extends SnowflakeBase {
this.id = data.id ?? this.id
}
}
export class GuildChannel extends Channel {
guildID: string
name: string
position: number
permissionOverwrites: OverwritePayload[]
guild: Guild
nsfw: boolean
parentID?: string
constructor(client: Client, data: GuildChannelPayloads, guild: Guild) {
super(client, data)
this.guildID = data.guild_id
this.name = data.name
this.guild = guild
this.position = data.position
this.permissionOverwrites = data.permission_overwrites
this.nsfw = data.nsfw
this.parentID = data.parent_id
}
readFromData(data: GuildChannelPayloads): void {
super.readFromData(data)
this.guildID = data.guild_id ?? this.guildID
this.name = data.name ?? this.name
this.position = data.position ?? this.position
this.permissionOverwrites =
data.permission_overwrites ?? this.permissionOverwrites
this.nsfw = data.nsfw ?? this.nsfw
this.parentID = data.parent_id ?? this.parentID
}
/** Get Permission Overties for a specific Member or Role */
async overwritesFor(target: Member | Role | string): Promise<Overwrite[]> {
const stringToObject =
typeof target === 'string'
? (await this.guild.members.get(target)) ??
(await this.guild.roles.get(target))
: target
if (stringToObject === undefined) {
throw new Error('Member or Role not found')
} else {
target = stringToObject
}
const roles =
target instanceof Member ? await target.roles.array() : undefined
const overwrites: Overwrite[] = []
for (const overwrite of this.permissionOverwrites) {
if (
overwrite.id === this.guild.id ||
roles?.some((e) => e.id === overwrite.id) === true ||
overwrite.id === target.id
) {
const id =
(await this.guild.members.get(overwrite.id)) ??
(await this.guild.roles.get(overwrite.id)) ??
overwrite.id
const allow = new Permissions(overwrite.allow)
const deny = new Permissions(overwrite.deny)
overwrites.push({
id,
type: overwrite.type,
allow,
deny
})
}
}
return overwrites
}
/** Get Permissions for a Member in this Channel */
async permissionsFor(target: Member | Role | string): Promise<Permissions> {
const id = typeof target === 'string' ? target : target.id
if (id === this.guild.ownerID) return new Permissions(Permissions.ALL)
const stringToObject =
typeof target === 'string'
? (await this.guild.members.get(target)) ??
(await this.guild.roles.get(target))
: target
if (stringToObject === undefined) {
throw new Error('Member or Role not found')
} else {
target = stringToObject
}
if (target.permissions.has('ADMINISTRATOR') === true)
return new Permissions(Permissions.ALL)
const overwrites = await this.overwritesFor(target)
const everyoneOW = overwrites.find((e) => e.id === this.guild.id)
const roleOWs = overwrites.filter((e) => e.type === 0)
const memberOWs = overwrites.filter((e) => e.type === 1)
return target.permissions
.remove(everyoneOW !== undefined ? Number(everyoneOW.deny) : 0)
.add(everyoneOW !== undefined ? Number(everyoneOW.allow) : 0)
.remove(roleOWs.length === 0 ? 0 : roleOWs.map((e) => Number(e.deny)))
.add(roleOWs.length === 0 ? 0 : roleOWs.map((e) => Number(e.allow)))
.remove(memberOWs.length === 0 ? 0 : memberOWs.map((e) => Number(e.deny)))
.add(memberOWs.length === 0 ? 0 : memberOWs.map((e) => Number(e.allow)))
}
async edit(options?: ModifyChannelOption): Promise<GuildChannels> {
const body: ModifyChannelPayload = {
name: options?.name,
position: options?.position,
permission_overwrites: options?.permissionOverwrites,
parent_id: options?.parentID,
nsfw: options?.nsfw
}
const resp = await this.client.rest.patch(CHANNEL(this.id), body)
return (
(getChannelByType(this.client, resp, this.guild) as
| GuildChannels
| undefined) ?? new GuildChannel(this.client, resp, this.guild)
)
}
/** Edit name of the channel */
async setName(name: string): Promise<GuildChannels> {
return await this.edit({ name })
}
/** Edit NSFW property of the channel */
async setNSFW(nsfw: boolean): Promise<GuildChannels> {
return await this.edit({ nsfw })
}
/** Set Permission Overwrites of the Channel */
async setOverwrites(overwrites: OverwriteAsArg[]): Promise<GuildChannels> {
const result = overwrites.map(
(overwrite): OverwritePayload => {
const id =
typeof overwrite.id === 'string' ? overwrite.id : overwrite.id.id
const allow =
typeof overwrite.allow === 'string'
? overwrite.allow
: overwrite.allow?.toJSON() ?? '0'
const deny =
typeof overwrite.deny === 'string'
? overwrite.deny
: overwrite.deny?.toJSON() ?? '0'
const type =
overwrite.id instanceof Role
? 0
: overwrite.id instanceof Member
? 1
: overwrite.type
if (type === undefined) {
throw new Error('Overwrite type is undefined.')
}
return {
id,
type,
allow,
deny
}
}
)
return await this.edit({ permissionOverwrites: result })
}
/** Add a Permission Overwrite */
async addOverwrite(overwrite: OverwriteAsArg): Promise<GuildChannels> {
const overwrites = this.permissionOverwrites
const id = typeof overwrite.id === 'string' ? overwrite.id : overwrite.id.id
const allow =
typeof overwrite.allow === 'string'
? overwrite.allow
: overwrite.allow?.toJSON() ?? '0'
const deny =
typeof overwrite.deny === 'string'
? overwrite.deny
: overwrite.deny?.toJSON() ?? '0'
const type =
overwrite.id instanceof Role
? 0
: overwrite.id instanceof Member
? 1
: overwrite.type
if (type === undefined) {
throw new Error('Overwrite type is undefined.')
}
overwrites.push({
id,
type,
allow,
deny
})
return await this.edit({ permissionOverwrites: overwrites })
}
/** Remove a Permission Overwrite */
async removeOverwrite(
target: Member | Role | string
): Promise<GuildChannels> {
target = typeof target === 'string' ? target : target.id
if (this.permissionOverwrites.find((e) => e.id === target) === undefined)
throw new Error('Permission Overwrite not found')
const overwrites = this.permissionOverwrites.filter((e) => e.id !== target)
return await this.edit({ permissionOverwrites: overwrites })
}
/** Edit a Permission Overwrite */
async editOverwrite(
overwrite: OverwriteAsArg,
{
overriteAllow = OverrideType.ADD,
overriteDeny = OverrideType.ADD
}: {
overriteAllow: OverrideType
overriteDeny: OverrideType
}
): Promise<GuildChannels> {
const id = typeof overwrite.id === 'string' ? overwrite.id : overwrite.id.id
const index = this.permissionOverwrites.findIndex((e) => e.id === id)
if (index < 0) throw new Error('Permission Overwrite not found')
const overwrites = this.permissionOverwrites
let allow: string
let deny: string
if (
overwrite.allow !== undefined &&
overriteAllow !== OverrideType.REPLACE
) {
switch (overriteAllow) {
case OverrideType.ADD: {
const originalAllow = new Permissions(overwrites[index].allow)
const newAllow = new Permissions(overwrite.allow)
allow = originalAllow.add([newAllow]).toJSON()
break
}
case OverrideType.REMOVE: {
const originalAllow = new Permissions(overwrites[index].allow)
const newAllow = new Permissions(overwrite.allow)
allow = originalAllow.remove([newAllow]).toJSON()
break
}
}
} else {
allow =
typeof overwrite.allow === 'string'
? overwrite.allow
: overwrite.allow?.toJSON() ?? overwrites[index].allow
}
if (overwrite.deny !== undefined && overriteDeny !== OverrideType.REPLACE) {
switch (overriteDeny) {
case OverrideType.ADD: {
const originalDeny = new Permissions(overwrites[index].deny)
const newDeny = new Permissions(overwrite.deny)
deny = originalDeny.add([newDeny]).toJSON()
break
}
case OverrideType.REMOVE: {
const originalDeny = new Permissions(overwrites[index].deny)
const newDeny = new Permissions(overwrite.deny)
deny = originalDeny.remove([newDeny]).toJSON()
break
}
}
} else {
deny =
typeof overwrite.deny === 'string'
? overwrite.deny
: overwrite.deny?.toJSON() ?? overwrites[index].deny
}
const type =
overwrite.id instanceof Role
? 0
: overwrite.id instanceof Member
? 1
: overwrite.type
if (type === undefined) {
throw new Error('Overwrite type is undefined.')
}
overwrites[index] = {
id,
type,
allow,
deny
}
return await this.edit({ permissionOverwrites: overwrites })
}
/** Edit position of the channel */
async setPosition(position: number): Promise<GuildChannels> {
return await this.edit({ position })
}
}

View File

@ -1,42 +1,14 @@
import { Client } from '../models/client.ts'
import { Channel } from './channel.ts'
import { GuildChannel } from './channel.ts'
import {
GuildCategoryChannelPayload,
ModifyGuildCategoryChannelOption,
ModifyGuildCategoryChannelPayload,
Overwrite
ModifyGuildCategoryChannelPayload
} from '../types/channel.ts'
import { Guild } from './guild.ts'
import { CHANNEL } from '../types/endpoint.ts'
export class CategoryChannel extends Channel {
guildID: string
name: string
position: number
permissionOverwrites: Overwrite[]
guild: Guild
parentID?: string
constructor(client: Client, data: GuildCategoryChannelPayload, guild: Guild) {
super(client, data)
this.guildID = data.guild_id
this.name = data.name
this.guild = guild
this.position = data.position
this.permissionOverwrites = data.permission_overwrites
this.parentID = data.parent_id
// TODO: Cache in Gateway Event Code
// cache.set('guildcategorychannel', this.id, this)
}
export class CategoryChannel extends GuildChannel {
readFromData(data: GuildCategoryChannelPayload): void {
super.readFromData(data)
this.guildID = data.guild_id ?? this.guildID
this.name = data.name ?? this.name
this.position = data.position ?? this.position
this.permissionOverwrites =
data.permission_overwrites ?? this.permissionOverwrites
this.parentID = data.parent_id ?? this.parentID
}
async edit(

View File

@ -1,61 +1,3 @@
import { Client } from '../models/client.ts'
import {
GuildNewsChannelPayload,
ModifyGuildNewsChannelOption,
ModifyGuildNewsChannelPayload,
Overwrite
} from '../types/channel.ts'
import { CHANNEL } from '../types/endpoint.ts'
import { Guild } from './guild.ts'
import { TextChannel } from './textChannel.ts'
import { GuildTextBasedChannel } from './guildTextChannel.ts'
export class NewsChannel extends TextChannel {
guildID: string
guild: Guild
name: string
position: number
permissionOverwrites: Overwrite[]
nsfw: boolean
parentID?: string
topic?: string
constructor(client: Client, data: GuildNewsChannelPayload, guild: Guild) {
super(client, data)
this.guildID = data.guild_id
this.name = data.name
this.guild = guild
this.position = data.position
this.permissionOverwrites = data.permission_overwrites
this.nsfw = data.nsfw
this.parentID = data.parent_id
this.topic = data.topic
}
readFromData(data: GuildNewsChannelPayload): void {
super.readFromData(data)
this.guildID = data.guild_id ?? this.guildID
this.name = data.name ?? this.name
this.position = data.position ?? this.position
this.permissionOverwrites =
data.permission_overwrites ?? this.permissionOverwrites
this.nsfw = data.nsfw ?? this.nsfw
this.parentID = data.parent_id ?? this.parentID
this.topic = data.topic ?? this.topic
}
async edit(options?: ModifyGuildNewsChannelOption): Promise<NewsChannel> {
const body: ModifyGuildNewsChannelPayload = {
name: options?.name,
position: options?.position,
permission_overwrites: options?.permissionOverwrites,
parent_id: options?.parentID,
type: options?.type,
topic: options?.topic,
nsfw: options?.nsfw
}
const resp = await this.client.rest.patch(CHANNEL(this.id), body)
return new NewsChannel(this.client, resp, this.guild)
}
}
export class NewsChannel extends GuildTextBasedChannel {}

View File

@ -0,0 +1,168 @@
import { Mixin } from '../../deps.ts'
import { TextChannel } from './textChannel.ts'
import { GuildChannel } from './channel.ts'
import { Client } from '../models/client.ts'
import {
ChannelTypes,
GuildTextBasedChannelPayload,
GuildTextChannelPayload,
ModifyGuildTextBasedChannelOption,
ModifyGuildTextBasedChannelPayload,
ModifyGuildTextChannelOption,
ModifyGuildTextChannelPayload
} from '../types/channel.ts'
import { Guild } from './guild.ts'
import { CHANNEL } from '../types/endpoint.ts'
import { Message } from './message.ts'
import { CreateInviteOptions } from '../managers/invites.ts'
import { Invite } from './invite.ts'
import { CategoryChannel } from './guildCategoryChannel.ts'
const GUILD_TEXT_BASED_CHANNEL_TYPES: ChannelTypes[] = [
ChannelTypes.GUILD_TEXT,
ChannelTypes.GUILD_NEWS
]
/** Represents a Text Channel but in a Guild */
export class GuildTextBasedChannel extends Mixin(TextChannel, GuildChannel) {
topic?: string
get mention(): string {
return `<#${this.id}>`
}
toString(): string {
return this.mention
}
constructor(
client: Client,
data: GuildTextBasedChannelPayload,
guild: Guild
) {
super(client, data, guild)
this.topic = data.topic
}
readFromData(data: GuildTextBasedChannelPayload): void {
super.readFromData(data)
this.topic = data.topic ?? this.topic
}
/** Edit the Guild Text Channel */
async edit(
options?: ModifyGuildTextBasedChannelOption
): Promise<GuildTextBasedChannel> {
const body: ModifyGuildTextBasedChannelPayload = {
name: options?.name,
position: options?.position,
permission_overwrites: options?.permissionOverwrites,
parent_id: options?.parentID,
nsfw: options?.nsfw,
topic: options?.topic
// rate_limit_per_user: options?.slowmode
}
const resp = await this.client.rest.patch(CHANNEL(this.id), body)
return new GuildTextBasedChannel(this.client, resp, this.guild)
}
/**
* Bulk Delete Messages in a Guild Text Channel
* @param messages Messages to delete. Can be a number, or Array of Message or IDs
*/
async bulkDelete(
messages: Array<Message | string> | number
): Promise<GuildTextBasedChannel> {
let ids: string[] = []
if (Array.isArray(messages))
ids = messages.map((e) => (typeof e === 'string' ? e : e.id))
else {
let list = await this.messages.array()
if (list.length < messages) list = (await this.fetchMessages()).array()
ids = list
.sort((b, a) => a.createdAt.getTime() - b.createdAt.getTime())
.filter((e, i) => i < messages)
.filter(
(e) =>
new Date().getTime() - e.createdAt.getTime() <=
1000 * 60 * 60 * 24 * 14
)
.map((e) => e.id)
}
ids = [...new Set(ids)]
if (ids.length < 2 || ids.length > 100)
throw new Error('bulkDelete can only delete messages in range 2-100')
await this.client.rest.api.channels[this.id].messages['bulk-delete'].post({
messages: ids
})
return this
}
/** Create an Invite for this Channel */
async createInvite(options?: CreateInviteOptions): Promise<Invite> {
return this.guild.invites.create(this.id, options)
}
/** Edit topic of the channel */
async setTopic(topic: string): Promise<GuildTextBasedChannel> {
return await this.edit({ topic })
}
/** Edit category of the channel */
async setCategory(
category: CategoryChannel | string
): Promise<GuildTextBasedChannel> {
return await this.edit({
parentID: typeof category === 'object' ? category.id : category
})
}
}
export const checkGuildTextBasedChannel = (
channel: TextChannel
): channel is GuildTextBasedChannel =>
GUILD_TEXT_BASED_CHANNEL_TYPES.includes(channel.type)
export class GuildTextChannel extends GuildTextBasedChannel {
slowmode: number
constructor(client: Client, data: GuildTextChannelPayload, guild: Guild) {
super(client, data, guild)
this.slowmode = data.rate_limit_per_user
}
readFromData(data: GuildTextChannelPayload): void {
super.readFromData(data)
this.slowmode = data.rate_limit_per_user ?? this.slowmode
}
/** Edit the Guild Text Channel */
async edit(
options?: ModifyGuildTextChannelOption
): Promise<GuildTextChannel> {
const body: ModifyGuildTextChannelPayload = {
name: options?.name,
position: options?.position,
permission_overwrites: options?.permissionOverwrites,
parent_id: options?.parentID,
nsfw: options?.nsfw,
topic: options?.topic,
rate_limit_per_user: options?.slowmode
}
const resp = await this.client.rest.patch(CHANNEL(this.id), body)
return new GuildTextChannel(this.client, resp, this.guild)
}
/** Edit Slowmode of the channel */
async setSlowmode(slowmode?: number | null): Promise<GuildTextChannel> {
return await this.edit({ slowmode: slowmode ?? null })
}
}

View File

@ -4,11 +4,10 @@ import { Client } from '../models/client.ts'
import {
GuildVoiceChannelPayload,
ModifyVoiceChannelOption,
ModifyVoiceChannelPayload,
Overwrite
ModifyVoiceChannelPayload
} from '../types/channel.ts'
import { CHANNEL } from '../types/endpoint.ts'
import { Channel } from './channel.ts'
import { GuildChannel } from './channel.ts'
import { Guild } from './guild.ts'
import { VoiceState } from './voiceState.ts'
@ -16,26 +15,14 @@ export interface VoiceServerData extends VoiceServerUpdateData {
sessionID: string
}
export class VoiceChannel extends Channel {
export class VoiceChannel extends GuildChannel {
bitrate: string
userLimit: number
guildID: string
name: string
guild: Guild
position: number
permissionOverwrites: Overwrite[]
parentID?: string
constructor(client: Client, data: GuildVoiceChannelPayload, guild: Guild) {
super(client, data)
super(client, data, guild)
this.bitrate = data.bitrate
this.userLimit = data.user_limit
this.guildID = data.guild_id
this.name = data.name
this.position = data.position
this.guild = guild
this.permissionOverwrites = data.permission_overwrites
this.parentID = data.parent_id
}
/** Join the Voice Channel */
@ -104,12 +91,6 @@ export class VoiceChannel extends Channel {
super.readFromData(data)
this.bitrate = data.bitrate ?? this.bitrate
this.userLimit = data.user_limit ?? this.userLimit
this.guildID = data.guild_id ?? this.guildID
this.name = data.name ?? this.name
this.position = data.position ?? this.position
this.permissionOverwrites =
data.permission_overwrites ?? this.permissionOverwrites
this.parentID = data.parent_id ?? this.parentID
}
async edit(options?: ModifyVoiceChannelOption): Promise<VoiceChannel> {

View File

@ -13,7 +13,8 @@ import { Member } from './member.ts'
import { Embed } from './embed.ts'
import { CHANNEL_MESSAGE } from '../types/endpoint.ts'
import { MessageMentions } from './messageMentions.ts'
import { GuildTextChannel, TextChannel } from './textChannel.ts'
import { TextChannel } from './textChannel.ts'
import { GuildTextBasedChannel } from './guildTextChannel.ts'
import { Guild } from './guild.ts'
import { MessageReactionsManager } from '../managers/messageReactions.ts'
import { MessageSticker } from './messageSticker.ts'
@ -126,8 +127,10 @@ export class Message extends SnowflakeBase {
const newMember = await this.guild?.members.get(this.member?.id)
if (newMember !== undefined) this.member = newMember
}
if (((this.channel as unknown) as GuildTextChannel).guild !== undefined)
this.guild = ((this.channel as unknown) as GuildTextChannel).guild
if (
((this.channel as unknown) as GuildTextBasedChannel).guild !== undefined
)
this.guild = ((this.channel as unknown) as GuildTextBasedChannel).guild
if (this.guild !== undefined && this.guildID === undefined)
this.guildID = this.guild.id
}

View File

@ -1,7 +1,7 @@
import { Client } from '../models/client.ts'
import { MessagePayload } from '../types/channel.ts'
import { Collection } from '../utils/collection.ts'
import { GuildTextChannel } from './textChannel.ts'
import { GuildTextBasedChannel } from './guildTextChannel.ts'
import { Message } from './message.ts'
import { Role } from './role.ts'
import { User } from './user.ts'
@ -11,7 +11,7 @@ export class MessageMentions {
message: Message
users: Collection<string, User> = new Collection()
roles: Collection<string, Role> = new Collection()
channels: Collection<string, GuildTextChannel> = new Collection()
channels: Collection<string, GuildTextBasedChannel> = new Collection()
everyone: boolean = false
static EVERYONE_MENTION = /@(everyone|here)/g
@ -39,7 +39,7 @@ export class MessageMentions {
}
if (payload.mention_channels !== undefined) {
for (const mentionChannel of payload.mention_channels) {
const channel = await this.client.channels.get<GuildTextChannel>(
const channel = await this.client.channels.get<GuildTextBasedChannel>(
mentionChannel.id
)
if (channel !== undefined) this.channels.set(channel.id, channel)
@ -51,7 +51,7 @@ export class MessageMentions {
if (matchChannels !== null) {
for (const id of matchChannels) {
const parsedID = id.substr(2, id.length - 3)
const channel = await this.client.channels.get<GuildTextChannel>(
const channel = await this.client.channels.get<GuildTextBasedChannel>(
parsedID
)
if (channel !== undefined) this.channels.set(channel.id, channel)

View File

@ -13,7 +13,8 @@ import { Embed } from './embed.ts'
import { Guild } from './guild.ts'
import { Member } from './member.ts'
import { Message } from './message.ts'
import { GuildTextChannel, TextChannel } from './textChannel.ts'
import { TextChannel } from './textChannel.ts'
import { GuildTextBasedChannel } from './guildTextChannel.ts'
import { User } from './user.ts'
import { Webhook } from './webhook.ts'
@ -46,7 +47,7 @@ export class Interaction extends SnowflakeBase {
token: string
id: string
data: InteractionData
channel: GuildTextChannel
channel: GuildTextBasedChannel
guild: Guild
member: Member
_savedHook?: Webhook
@ -55,7 +56,7 @@ export class Interaction extends SnowflakeBase {
client: Client,
data: InteractionPayload,
others: {
channel: GuildTextChannel
channel: GuildTextBasedChannel
guild: Guild
member: Member
}

View File

@ -1,28 +1,18 @@
import { CreateInviteOptions } from '../managers/invites.ts'
import { MessagesManager } from '../managers/messages.ts'
import { Client } from '../models/client.ts'
import {
GuildTextChannelPayload,
MessageOptions,
MessagePayload,
ModifyGuildTextChannelOption,
ModifyGuildTextChannelPayload,
Overwrite,
TextChannelPayload
} from '../types/channel.ts'
import {
CHANNEL,
MESSAGE_REACTION_ME,
MESSAGE_REACTION_USER
} from '../types/endpoint.ts'
import { Collection } from '../utils/collection.ts'
import { Permissions } from '../utils/permissions.ts'
import { Channel } from './channel.ts'
import { Embed } from './embed.ts'
import { Emoji } from './emoji.ts'
import { Guild } from './guild.ts'
import { CategoryChannel } from './guildCategoryChannel.ts'
import { Invite } from './invite.ts'
import { Member } from './member.ts'
import { Message } from './message.ts'
import { User } from './user.ts'
@ -185,225 +175,3 @@ export class TextChannel extends Channel {
return this
}
}
/** Represents a Text Channel but in a Guild */
export class GuildTextChannel extends TextChannel {
guildID: string
name: string
position: number
permissionOverwrites: Overwrite[]
nsfw: boolean
parentID?: string
slowmode: number
topic?: string
guild: Guild
get mention(): string {
return `<#${this.id}>`
}
toString(): string {
return this.mention
}
constructor(client: Client, data: GuildTextChannelPayload, guild: Guild) {
super(client, data)
this.guildID = data.guild_id
this.name = data.name
this.guild = guild
this.position = data.position
this.permissionOverwrites = data.permission_overwrites
this.nsfw = data.nsfw
this.parentID = data.parent_id
this.topic = data.topic
this.slowmode = data.rate_limit_per_user
}
readFromData(data: GuildTextChannelPayload): void {
super.readFromData(data)
this.guildID = data.guild_id ?? this.guildID
this.name = data.name ?? this.name
this.position = data.position ?? this.position
this.permissionOverwrites =
data.permission_overwrites ?? this.permissionOverwrites
this.nsfw = data.nsfw ?? this.nsfw
this.parentID = data.parent_id ?? this.parentID
this.topic = data.topic ?? this.topic
this.slowmode = data.rate_limit_per_user ?? this.slowmode
}
/** Edit the Guild Text Channel */
async edit(
options?: ModifyGuildTextChannelOption
): Promise<GuildTextChannel> {
const body: ModifyGuildTextChannelPayload = {
name: options?.name,
position: options?.position,
permission_overwrites: options?.permissionOverwrites,
parent_id: options?.parentID,
nsfw: options?.nsfw,
topic: options?.topic,
rate_limit_per_user: options?.slowmode
}
const resp = await this.client.rest.patch(CHANNEL(this.id), body)
return new GuildTextChannel(this.client, resp, this.guild)
}
/**
* Bulk Delete Messages in a Guild Text Channel
* @param messages Messages to delete. Can be a number, or Array of Message or IDs
*/
async bulkDelete(
messages: Array<Message | string> | number
): Promise<GuildTextChannel> {
let ids: string[] = []
if (Array.isArray(messages))
ids = messages.map((e) => (typeof e === 'string' ? e : e.id))
else {
let list = await this.messages.array()
if (list.length < messages) list = (await this.fetchMessages()).array()
ids = list
.sort((b, a) => a.createdAt.getTime() - b.createdAt.getTime())
.filter((e, i) => i < messages)
.filter(
(e) =>
new Date().getTime() - e.createdAt.getTime() <=
1000 * 60 * 60 * 24 * 14
)
.map((e) => e.id)
}
ids = [...new Set(ids)]
if (ids.length < 2 || ids.length > 100)
throw new Error('bulkDelete can only delete messages in range 2-100')
await this.client.rest.api.channels[this.id].messages['bulk-delete'].post({
messages: ids
})
return this
}
/** Create an Invite for this Channel */
async createInvite(options?: CreateInviteOptions): Promise<Invite> {
return this.guild.invites.create(this.id, options)
}
/** Get Permission Overties for a specific Member */
async overwritesFor(member: Member | string): Promise<Overwrite[]> {
member = (typeof member === 'string'
? await this.guild.members.get(member)
: member) as Member
if (member === undefined) throw new Error('Member not found')
const roles = await member.roles.array()
const overwrites: Overwrite[] = []
for (const overwrite of this.permissionOverwrites) {
if (overwrite.id === this.guild.id) {
overwrites.push(overwrite)
} else if (roles.some((e) => e.id === overwrite.id) === true) {
overwrites.push(overwrite)
} else if (overwrite.id === member.id) {
overwrites.push(overwrite)
}
}
return overwrites
}
/** Get Permissions for a Member in this Channel */
async permissionsFor(member: Member | string): Promise<Permissions> {
const id = typeof member === 'string' ? member : member.id
if (id === this.guild.ownerID) return new Permissions(Permissions.ALL)
member = (typeof member === 'string'
? await this.guild.members.get(member)
: member) as Member
if (member === undefined) throw new Error('Member not found')
if (member.permissions.has('ADMINISTRATOR') === true)
return new Permissions(Permissions.ALL)
const overwrites = await this.overwritesFor(member)
const everyoneOW = overwrites.find((e) => e.id === this.guild.id)
const roleOWs = overwrites.filter((e) => e.type === 0)
const memberOWs = overwrites.filter((e) => e.type === 1)
return member.permissions
.remove(everyoneOW !== undefined ? Number(everyoneOW.deny) : 0)
.add(everyoneOW !== undefined ? Number(everyoneOW.allow) : 0)
.remove(roleOWs.length === 0 ? 0 : roleOWs.map((e) => Number(e.deny)))
.add(roleOWs.length === 0 ? 0 : roleOWs.map((e) => Number(e.allow)))
.remove(memberOWs.length === 0 ? 0 : memberOWs.map((e) => Number(e.deny)))
.add(memberOWs.length === 0 ? 0 : memberOWs.map((e) => Number(e.allow)))
}
/** Edit name of the channel */
async setName(name: string): Promise<GuildTextChannel> {
return await this.edit({ name })
}
/** Edit topic of the channel */
async setTopic(topic: string): Promise<GuildTextChannel> {
return await this.edit({ topic })
}
/** Edit topic of the channel */
async setCategory(
category: CategoryChannel | string
): Promise<GuildTextChannel> {
return await this.edit({
parentID: typeof category === 'object' ? category.id : category
})
}
/** Edit position of the channel */
async setPosition(position: number): Promise<GuildTextChannel> {
return await this.edit({ position })
}
/** Edit Slowmode of the channel */
async setSlowmode(slowmode?: number | null): Promise<GuildTextChannel> {
return await this.edit({ slowmode: slowmode ?? null })
}
/** Edit NSFW property of the channel */
async setNSFW(nsfw: boolean): Promise<GuildTextChannel> {
return await this.edit({ nsfw })
}
/** Set Permission Overwrites of the Channel */
async setOverwrites(overwrites: Overwrite[]): Promise<GuildTextChannel> {
return await this.edit({ permissionOverwrites: overwrites })
}
/** Add a Permission Overwrite */
async addOverwrite(overwrite: Overwrite): Promise<GuildTextChannel> {
const overwrites = this.permissionOverwrites
overwrites.push(overwrite)
return await this.edit({ permissionOverwrites: overwrites })
}
/** Remove a Permission Overwrite */
async removeOverwrite(id: string): Promise<GuildTextChannel> {
if (this.permissionOverwrites.findIndex((e) => e.id === id) < 0)
throw new Error('Permission Overwrite not found')
const overwrites = this.permissionOverwrites.filter((e) => e.id !== id)
return await this.edit({ permissionOverwrites: overwrites })
}
/** Edit a Permission Overwrite */
async editOverwrite(overwrite: Overwrite): Promise<GuildTextChannel> {
const index = this.permissionOverwrites.findIndex(
(e) => e.id === overwrite.id
)
if (index < 0) throw new Error('Permission Overwrite not found')
const overwrites = this.permissionOverwrites
overwrites[index] = overwrite
return await this.edit({ permissionOverwrites: overwrites })
}
}

View File

@ -9,11 +9,12 @@ import {
Guild,
EveryChannelTypes,
ChannelTypes,
GuildTextChannel
GuildTextChannel,
checkGuildTextBasedChannel,
Permissions
} from '../../mod.ts'
import { Collector } from '../models/collectors.ts'
import { MessageAttachment } from '../structures/message.ts'
import { Permissions } from '../utils/permissions.ts'
import { TOKEN } from './config.ts'
const client = new Client({
@ -190,7 +191,8 @@ client.on('messageCreate', async (msg: Message) => {
if (typeof vs !== 'object') return
vs.channel?.join()
} else if (msg.content === '!getOverwrites') {
if (msg.channel.type !== ChannelTypes.GUILD_TEXT) {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!checkGuildTextBasedChannel(msg.channel)) {
return msg.channel.send("This isn't a guild text channel!")
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
@ -202,16 +204,15 @@ client.on('messageCreate', async (msg: Message) => {
`Your permission overwrites:\n${overwrites
.map(
(over) =>
`ID: ${over.id}\nAllowed:\n${new Permissions(over.allow)
`ID: ${over.id}\nAllowed:\n${over.allow
.toArray()
.join('\n')}\nDenied:\n${new Permissions(over.deny)
.toArray()
.join('\n')}`
.join('\n')}\nDenied:\n${over.deny.toArray().join('\n')}`
)
.join('\n\n')}`
)
} else if (msg.content === '!getPermissions') {
if (msg.channel.type !== ChannelTypes.GUILD_TEXT) {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!checkGuildTextBasedChannel(msg.channel)) {
return msg.channel.send("This isn't a guild text channel!")
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
@ -220,6 +221,18 @@ client.on('messageCreate', async (msg: Message) => {
msg.member as Member
)
msg.channel.send(`Your permissions:\n${permissions.toArray().join('\n')}`)
} else if (msg.content === '!addBasicOverwrites') {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!checkGuildTextBasedChannel(msg.channel)) {
return msg.channel.send("This isn't a guild text channel!")
}
if (msg.member !== undefined) {
await msg.channel.addOverwrite({
id: msg.member,
allow: Permissions.DEFAULT.toString()
})
msg.channel.send(`Done!`)
}
} else if (msg.content === '!addAllRoles') {
const roles = await msg.guild?.roles.array()
if (roles !== undefined) {

View File

@ -1,5 +1,8 @@
import { Embed } from '../structures/embed.ts'
import type { Message, MessageAttachment } from '../structures/message.ts'
import { Member } from '../structures/member.ts'
import { Message, MessageAttachment } from '../structures/message.ts'
import { Role } from '../structures/role.ts'
import { Permissions } from '../utils/permissions.ts'
import { EmojiPayload } from './emoji.ts'
import { MemberPayload } from './guild.ts'
import { UserPayload } from './user.ts'
@ -18,25 +21,23 @@ export interface GuildChannelPayload extends ChannelPayload {
guild_id: string
name: string
position: number
permission_overwrites: Overwrite[]
permission_overwrites: OverwritePayload[]
nsfw: boolean
parent_id?: string
}
export interface GuildTextChannelPayload
export interface GuildTextBasedChannelPayload
extends TextChannelPayload,
GuildChannelPayload {
nsfw: boolean
rate_limit_per_user: number
topic?: string
}
export interface GuildNewsChannelPayload
extends TextChannelPayload,
GuildChannelPayload {
topic?: string
nsfw: boolean
export interface GuildTextChannelPayload extends GuildTextBasedChannelPayload {
rate_limit_per_user: number
}
export interface GuildNewsChannelPayload extends GuildTextBasedChannelPayload {}
export interface GuildVoiceChannelPayload extends GuildChannelPayload {
bitrate: string
user_limit: number
@ -59,25 +60,27 @@ export interface GuildCategoryChannelPayload
export interface ModifyChannelPayload {
name?: string
position?: number | null
permission_overwrites?: Overwrite[] | null
permission_overwrites?: OverwritePayload[] | null
parent_id?: string
nsfw?: boolean | null
}
export interface ModifyGuildCategoryChannelPayload
extends ModifyChannelPayload {}
export interface ModifyGuildTextChannelPayload extends ModifyChannelPayload {
export interface ModifyGuildTextBasedChannelPayload
extends ModifyChannelPayload {
type?: number
topic?: string | null
nsfw?: boolean | null
}
export interface ModifyGuildTextChannelPayload
extends ModifyGuildTextBasedChannelPayload {
rate_limit_per_user?: number | null
}
export interface ModifyGuildNewsChannelPayload extends ModifyChannelPayload {
type?: number
topic?: string | null
nsfw?: boolean | null
}
export interface ModifyGuildNewsChannelPayload
extends ModifyGuildTextBasedChannelPayload {}
export interface ModifyVoiceChannelPayload extends ModifyChannelPayload {
bitrate?: number | null
@ -87,37 +90,65 @@ export interface ModifyVoiceChannelPayload extends ModifyChannelPayload {
export interface ModifyChannelOption {
name?: string
position?: number | null
permissionOverwrites?: Overwrite[] | null
permissionOverwrites?: OverwritePayload[] | null
parentID?: string
nsfw?: boolean | null
}
export interface ModifyGuildCategoryChannelOption extends ModifyChannelOption {}
export interface ModifyGuildTextChannelOption extends ModifyChannelOption {
export interface ModifyGuildTextBasedChannelOption extends ModifyChannelOption {
type?: number
topic?: string | null
nsfw?: boolean | null
}
export interface ModifyGuildTextChannelOption
extends ModifyGuildTextBasedChannelOption {
slowmode?: number | null
}
export interface ModifyGuildNewsChannelOption extends ModifyChannelOption {
type?: number
topic?: string | null
nsfw?: boolean | null
}
export interface ModifyGuildNewsChannelOption
extends ModifyGuildTextBasedChannelOption {}
export interface ModifyVoiceChannelOption extends ModifyChannelOption {
bitrate?: number | null
userLimit?: number | null
}
export interface Overwrite {
export enum OverwriteType {
ROLE = 0,
USER = 1
}
export interface OverwritePayload {
id: string
type: number
type: OverwriteType
allow: string
deny: string
}
export interface Overwrite {
id: string | Role | Member
type: OverwriteType
allow: Permissions
deny: Permissions
}
export interface OverwriteAsOptions {
id: string | Role | Member
type?: OverwriteType
allow?: string | Permissions
deny?: string | Permissions
}
export type OverwriteAsArg = OverwriteAsOptions | OverwritePayload
export enum OverrideType {
ADD = 0,
REMOVE = 1,
REPLACE = 2
}
export enum ChannelTypes {
GUILD_TEXT = 0,
DM = 1,

View File

@ -1,13 +1,20 @@
import { GuildChannel } from '../structures/channel.ts'
import { Emoji } from '../structures/emoji.ts'
import { CategoryChannel } from '../structures/guildCategoryChannel.ts'
import { NewsChannel } from '../structures/guildNewsChannel.ts'
import { VoiceChannel } from '../structures/guildVoiceChannel.ts'
import { Role } from '../structures/role.ts'
import { GuildTextChannel } from '../structures/textChannel.ts'
import {
GuildTextChannel,
GuildTextBasedChannel
} from '../structures/guildTextChannel.ts'
import { ApplicationPayload } from './application.ts'
import {
ChannelPayload,
ChannelTypes,
GuildCategoryChannelPayload,
GuildNewsChannelPayload,
GuildTextBasedChannelPayload,
GuildTextChannelPayload,
GuildVoiceChannelPayload
} from './channel.ts'
@ -171,11 +178,26 @@ export interface GuildWidgetPayload {
presence_count: number
}
export type GuildChannelPayloads =
export type GuildTextBasedPayloads =
| GuildTextBasedChannelPayload
| GuildTextChannelPayload
| GuildNewsChannelPayload
export type GuildChannelPayloads =
| GuildTextBasedPayloads
| GuildVoiceChannelPayload
| GuildCategoryChannelPayload
export type GuildChannels = GuildTextChannel | VoiceChannel | CategoryChannel
export type GuildTextBasedChannels =
| GuildTextBasedChannel
| GuildTextChannel
| NewsChannel
export type GuildChannels =
| GuildChannel
| GuildTextBasedChannels
| VoiceChannel
| CategoryChannel
export interface GuildCreatePayload {
name: string

View File

@ -6,6 +6,7 @@ import {
GroupDMChannelPayload,
GuildCategoryChannelPayload,
GuildNewsChannelPayload,
GuildTextBasedChannelPayload,
GuildTextChannelPayload,
GuildVoiceChannelPayload,
TextChannelPayload
@ -13,16 +14,21 @@ import {
import { DMChannel } from '../structures/dmChannel.ts'
import { GroupDMChannel } from '../structures/groupChannel.ts'
import { CategoryChannel } from '../structures/guildCategoryChannel.ts'
import {
GuildTextBasedChannel,
GuildTextChannel
} from '../structures/guildTextChannel.ts'
import { NewsChannel } from '../structures/guildNewsChannel.ts'
import { VoiceChannel } from '../structures/guildVoiceChannel.ts'
import { Guild } from '../structures/guild.ts'
import { GuildTextChannel, TextChannel } from '../structures/textChannel.ts'
import { Channel } from '../structures/channel.ts'
import { TextChannel } from '../structures/textChannel.ts'
import { Channel, GuildChannel } from '../structures/channel.ts'
export type EveryTextChannelTypes =
| TextChannel
| NewsChannel
| GuildTextChannel
| GuildTextBasedChannel
| DMChannel
| GroupDMChannel
@ -30,11 +36,13 @@ export type EveryTextChannelPayloadTypes =
| TextChannelPayload
| GuildNewsChannelPayload
| GuildTextChannelPayload
| GuildTextBasedChannelPayload
| DMChannelPayload
| GroupDMChannelPayload
export type EveryChannelTypes =
| Channel
| GuildChannel
| CategoryChannel
| VoiceChannel
| EveryTextChannelTypes