Merge pull request #119 from Helloyunho/voiceState-updates
Add things to `<VoiceState>` and Add `<GuildChannelVoiceStatesManager>`
This commit is contained in:
		
						commit
						01f92b35bf
					
				
					 7 changed files with 221 additions and 7 deletions
				
			
		
							
								
								
									
										39
									
								
								src/managers/guildChannelVoiceStates.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/managers/guildChannelVoiceStates.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | |||
| import { Client } from '../models/client.ts' | ||||
| import { BaseChildManager } from './baseChild.ts' | ||||
| import { VoiceStatePayload } from '../types/voice.ts' | ||||
| import { VoiceState } from '../structures/voiceState.ts' | ||||
| import { GuildVoiceStatesManager } from './guildVoiceStates.ts' | ||||
| import { VoiceChannel } from '../structures/guildVoiceChannel.ts' | ||||
| 
 | ||||
| export class GuildChannelVoiceStatesManager extends BaseChildManager< | ||||
|   VoiceStatePayload, | ||||
|   VoiceState | ||||
| > { | ||||
|   channel: VoiceChannel | ||||
| 
 | ||||
|   constructor( | ||||
|     client: Client, | ||||
|     parent: GuildVoiceStatesManager, | ||||
|     channel: VoiceChannel | ||||
|   ) { | ||||
|     super(client, parent as any) | ||||
|     this.channel = channel | ||||
|   } | ||||
| 
 | ||||
|   async get(id: string): Promise<VoiceState | undefined> { | ||||
|     const res = await this.parent.get(id) | ||||
|     if (res !== undefined && res.channel?.id === this.channel.id) return res | ||||
|     else return undefined | ||||
|   } | ||||
| 
 | ||||
|   async array(): Promise<VoiceState[]> { | ||||
|     const arr = (await this.parent.array()) as VoiceState[] | ||||
|     return arr.filter((c: any) => c.channel?.id === this.channel.id) as any | ||||
|   } | ||||
| 
 | ||||
|   async fromPayload(d: VoiceStatePayload[]): Promise<void> { | ||||
|     for (const data of d) { | ||||
|       await this.set(data.user_id, data) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -30,7 +30,7 @@ export class GuildVoiceStatesManager extends BaseManager< | |||
| 
 | ||||
|     const guild = | ||||
|       raw.guild_id === undefined | ||||
|         ? undefined | ||||
|         ? this.guild | ||||
|         : await this.client.guilds.get(raw.guild_id) | ||||
| 
 | ||||
|     return new VoiceState(this.client, raw, { | ||||
|  | @ -57,7 +57,7 @@ export class GuildVoiceStatesManager extends BaseManager< | |||
|       arr.map(async (raw) => { | ||||
|         const guild = | ||||
|           raw.guild_id === undefined | ||||
|             ? undefined | ||||
|             ? this.guild | ||||
|             : await this.client.guilds.get(raw.guild_id) | ||||
| 
 | ||||
|         return new VoiceState(this.client, raw, { | ||||
|  |  | |||
|  | @ -10,6 +10,9 @@ import { CHANNEL } from '../types/endpoint.ts' | |||
| import { GuildChannel } from './channel.ts' | ||||
| import { Guild } from './guild.ts' | ||||
| import { VoiceState } from './voiceState.ts' | ||||
| import { GuildChannelVoiceStatesManager } from '../managers/guildChannelVoiceStates.ts' | ||||
| import { User } from './user.ts' | ||||
| import { Member } from './member.ts' | ||||
| 
 | ||||
| export interface VoiceServerData extends VoiceServerUpdateData { | ||||
|   sessionID: string | ||||
|  | @ -18,6 +21,11 @@ export interface VoiceServerData extends VoiceServerUpdateData { | |||
| export class VoiceChannel extends GuildChannel { | ||||
|   bitrate: string | ||||
|   userLimit: number | ||||
|   voiceStates = new GuildChannelVoiceStatesManager( | ||||
|     this.client, | ||||
|     this.guild.voiceStates, | ||||
|     this | ||||
|   ) | ||||
| 
 | ||||
|   constructor(client: Client, data: GuildVoiceChannelPayload, guild: Guild) { | ||||
|     super(client, data, guild) | ||||
|  | @ -107,4 +115,25 @@ export class VoiceChannel extends GuildChannel { | |||
| 
 | ||||
|     return new VoiceChannel(this.client, resp, this.guild) | ||||
|   } | ||||
| 
 | ||||
|   async disconnectMember( | ||||
|     member: User | Member | string | ||||
|   ): Promise<Member | undefined> { | ||||
|     const memberID = typeof member === 'string' ? member : member.id | ||||
|     const memberVoiceState = await this.voiceStates.get(memberID) | ||||
| 
 | ||||
|     return memberVoiceState?.disconnect() | ||||
|   } | ||||
| 
 | ||||
|   async disconnectAll(): Promise<Member[]> { | ||||
|     const members: Member[] = [] | ||||
|     for await (const memberVoiceState of this.voiceStates) { | ||||
|       const member = await memberVoiceState.disconnect() | ||||
|       if (member !== undefined) { | ||||
|         members.push(member) | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     return members | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import { MemberPayload } from '../types/guild.ts' | |||
| import { Permissions } from '../utils/permissions.ts' | ||||
| import { SnowflakeBase } from './base.ts' | ||||
| import { Guild } from './guild.ts' | ||||
| import { VoiceChannel } from './guildVoiceChannel.ts' | ||||
| import { Role } from './role.ts' | ||||
| import { User } from './user.ts' | ||||
| 
 | ||||
|  | @ -13,6 +14,7 @@ export interface MemberData { | |||
|   roles?: Array<Role | string> | ||||
|   deaf?: boolean | ||||
|   mute?: boolean | ||||
|   channel?: string | VoiceChannel | null | ||||
| } | ||||
| 
 | ||||
| export class Member extends SnowflakeBase { | ||||
|  | @ -84,8 +86,11 @@ export class Member extends SnowflakeBase { | |||
|       nick: data.nick, | ||||
|       roles: data.roles?.map((e) => (typeof e === 'string' ? e : e.id)), | ||||
|       deaf: data.deaf, | ||||
|       mute: data.mute | ||||
|       mute: data.mute, | ||||
|       channel_id: | ||||
|         data.channel instanceof VoiceChannel ? data.channel.id : data.channel | ||||
|     } | ||||
| 
 | ||||
|     const res = await this.client.rest.patch( | ||||
|       GUILD_MEMBER(this.guild.id, this.id), | ||||
|       payload, | ||||
|  | @ -125,7 +130,7 @@ export class Member extends SnowflakeBase { | |||
|    */ | ||||
|   async setMute(mute?: boolean): Promise<Member> { | ||||
|     return await this.edit({ | ||||
|       mute: mute === undefined ? false : mute | ||||
|       mute: mute ?? false | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|  | @ -135,7 +140,28 @@ export class Member extends SnowflakeBase { | |||
|    */ | ||||
|   async setDeaf(deaf?: boolean): Promise<Member> { | ||||
|     return await this.edit({ | ||||
|       deaf: deaf === undefined ? false : deaf | ||||
|       deaf: deaf ?? false | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Moves a Member to another VC | ||||
|    * @param channel Channel to move(null or undefined to disconnect) | ||||
|    */ | ||||
|   async moveVoiceChannel( | ||||
|     channel?: string | VoiceChannel | null | ||||
|   ): Promise<Member> { | ||||
|     return await this.edit({ | ||||
|       channel: channel ?? null | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Disconnects a Member from connected VC | ||||
|    */ | ||||
|   async disconnectVoice(): Promise<Member> { | ||||
|     return await this.edit({ | ||||
|       channel: null | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|  | @ -146,6 +172,13 @@ export class Member extends SnowflakeBase { | |||
|     return await this.setMute(false) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Undeafs the Member from VC. | ||||
|    */ | ||||
|   async undeaf(): Promise<Member> { | ||||
|     return await this.setDeaf(false) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Kicks the member. | ||||
|    */ | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| import { Client } from '../models/client.ts' | ||||
| import { ChannelTypes } from '../types/channel.ts' | ||||
| import { VoiceStatePayload } from '../types/voice.ts' | ||||
| import { Base } from './base.ts' | ||||
| import { Guild } from './guild.ts' | ||||
|  | @ -52,12 +53,81 @@ export class VoiceState extends Base { | |||
|     this.deaf = data.deaf ?? this.deaf | ||||
|     this.channelID = data.channel_id ?? this.channelID | ||||
|     this.mute = data.mute ?? this.mute | ||||
|     this.deaf = data.self_deaf ?? this.deaf | ||||
|     this.mute = data.self_mute ?? this.mute | ||||
|     this.selfDeaf = data.self_deaf ?? this.selfDeaf | ||||
|     this.selfMute = data.self_mute ?? this.selfMute | ||||
|     this.stream = data.self_stream ?? this.stream | ||||
|     this.video = data.self_video ?? this.video | ||||
|     this.suppress = data.suppress ?? this.suppress | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Disconnects a Member from connected VC | ||||
|    */ | ||||
|   async disconnect(): Promise<Member | undefined> { | ||||
|     const result = this.member?.disconnectVoice() | ||||
|     if (result !== undefined) { | ||||
|       this.channelID = null | ||||
|       this.channel = null | ||||
|     } | ||||
|     return result | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Moves a Member to another VC | ||||
|    * @param channel Channel to move(null or undefined to disconnect) | ||||
|    */ | ||||
|   async moveChannel( | ||||
|     channel?: string | VoiceChannel | null | ||||
|   ): Promise<Member | undefined> { | ||||
|     const result = this.member?.moveVoiceChannel(channel) | ||||
|     if (result !== undefined) { | ||||
|       let channelFetched: VoiceChannel | null | ||||
|       let channelID: string | null | ||||
|       if (typeof channel === 'string') { | ||||
|         channelID = channel | ||||
|         const channelCached = await this.guild?.channels.fetch(channel) | ||||
|         if (channelCached?.type === ChannelTypes.GUILD_VOICE) { | ||||
|           channelFetched = channelCached as VoiceChannel | ||||
|         } else { | ||||
|           throw new Error(`Channel ${channel} is not a VoiceChannel.`) | ||||
|         } | ||||
|       } else { | ||||
|         channelID = channel?.id ?? null | ||||
|         channelFetched = channel ?? null | ||||
|       } | ||||
|       this.channelID = channelID | ||||
|       this.channel = channelFetched | ||||
|     } | ||||
|     return result | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Sets a Member mute in VC | ||||
|    * @param mute Value to set | ||||
|    */ | ||||
|   async setMute(mute?: boolean): Promise<Member | undefined> { | ||||
|     return this.member?.setMute(mute) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Sets a Member deaf in VC | ||||
|    * @param deaf Value to set | ||||
|    */ | ||||
|   async setDeaf(deaf?: boolean): Promise<Member | undefined> { | ||||
|     return this.member?.setDeaf(deaf) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Unmutes the Member from VC. | ||||
|    */ | ||||
|   async unmute(): Promise<Member | undefined> { | ||||
|     return await this.setMute(false) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Undeafs the Member from VC. | ||||
|    */ | ||||
|   async undeaf(): Promise<Member | undefined> { | ||||
|     return await this.setDeaf(false) | ||||
|   } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										23
									
								
								src/test/cmds/kickFromSpecificVoice.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/test/cmds/kickFromSpecificVoice.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| import { Command, VoiceChannel } from '../../../mod.ts' | ||||
| import { CommandContext } from '../../models/command.ts' | ||||
| import { ChannelTypes } from '../../types/channel.ts' | ||||
| 
 | ||||
| export default class KickFromSpecificVoiceCommand extends Command { | ||||
|   name = 'kickFromSpecificVoice' | ||||
| 
 | ||||
|   async execute(ctx: CommandContext): Promise<void> { | ||||
|     if (ctx.guild !== undefined) { | ||||
|       const channel = await ctx.guild.channels.get('YOUR VOICE CHANNEL ID') | ||||
|       if (channel === undefined || channel.type !== ChannelTypes.GUILD_VOICE) { | ||||
|         ctx.channel.send('The channel is either not a voice or not available.') | ||||
|         return | ||||
|       } | ||||
| 
 | ||||
|       // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
 | ||||
|       const members = await (channel as VoiceChannel).disconnectAll() | ||||
|       members.forEach((member) => { | ||||
|         ctx.channel.send(`Kicked member ${member.id}`) | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										20
									
								
								src/test/cmds/kickFromVoice.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/test/cmds/kickFromVoice.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| import { Command } from '../../../mod.ts' | ||||
| import { CommandContext } from '../../models/command.ts' | ||||
| 
 | ||||
| export default class KickFromVoiceCommand extends Command { | ||||
|   name = 'kickFromVoice' | ||||
| 
 | ||||
|   async execute(ctx: CommandContext): Promise<void> { | ||||
|     if (ctx.guild !== undefined) { | ||||
|       const voiceStates = await ctx.guild.voiceStates.array() | ||||
|       if (voiceStates !== undefined) { | ||||
|         voiceStates.forEach(async (voiceState) => { | ||||
|           const member = await voiceState.disconnect() | ||||
|           if (member !== undefined) { | ||||
|             ctx.channel.send(`Kicked member ${member.id}`) | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue