From 58fb2d8a9187fb9f43a0df53fd5f36a75d02cc83 Mon Sep 17 00:00:00 2001 From: AlTech98 Date: Sat, 4 Sep 2021 11:46:46 +0200 Subject: Added /guilds/:id/voice-states apis --- .../#guild_id/voice-states/#user_id/index.ts | 15 ++++++ .../guilds/#guild_id/voice-states/@me/index.ts | 15 ++++++ api/src/schema/Guild.ts | 12 +++++ api/src/util/VoiceState.ts | 54 ++++++++++++++++++++++ 4 files changed, 96 insertions(+) create mode 100644 api/src/routes/guilds/#guild_id/voice-states/#user_id/index.ts create mode 100644 api/src/routes/guilds/#guild_id/voice-states/@me/index.ts create mode 100644 api/src/util/VoiceState.ts (limited to 'api') diff --git a/api/src/routes/guilds/#guild_id/voice-states/#user_id/index.ts b/api/src/routes/guilds/#guild_id/voice-states/#user_id/index.ts new file mode 100644 index 00000000..02951f81 --- /dev/null +++ b/api/src/routes/guilds/#guild_id/voice-states/#user_id/index.ts @@ -0,0 +1,15 @@ +import { check } from "../../../../../util/instanceOf"; +import { VoiceStateUpdateSchema } from "../../../../../schema"; +import { Request, Response, Router } from "express"; +import { updateVoiceState } from "../../../../../util/VoiceState"; + +const router = Router(); + +router.patch("/", check(VoiceStateUpdateSchema), async (req: Request, res: Response) => { + const body = req.body as VoiceStateUpdateSchema; + const { guild_id, user_id } = req.params; + await updateVoiceState(body, guild_id, req.user_id, user_id) + return res.sendStatus(204); +}); + +export default router; \ No newline at end of file diff --git a/api/src/routes/guilds/#guild_id/voice-states/@me/index.ts b/api/src/routes/guilds/#guild_id/voice-states/@me/index.ts new file mode 100644 index 00000000..42ba543e --- /dev/null +++ b/api/src/routes/guilds/#guild_id/voice-states/@me/index.ts @@ -0,0 +1,15 @@ +import { check } from "../../../../../util/instanceOf"; +import { VoiceStateUpdateSchema } from "../../../../../schema"; +import { Request, Response, Router } from "express"; +import { updateVoiceState } from "../../../../../util/VoiceState"; + +const router = Router(); + +router.patch("/", check(VoiceStateUpdateSchema), async (req: Request, res: Response) => { + const body = req.body as VoiceStateUpdateSchema; + const { guild_id } = req.params; + await updateVoiceState(body, guild_id, req.user_id) + return res.sendStatus(204); +}); + +export default router; \ No newline at end of file diff --git a/api/src/schema/Guild.ts b/api/src/schema/Guild.ts index 7c96905e..29c78ab0 100644 --- a/api/src/schema/Guild.ts +++ b/api/src/schema/Guild.ts @@ -92,3 +92,15 @@ export interface GuildUpdateWelcomeScreenSchema { enabled?: boolean; description?: string; } + +export const VoiceStateUpdateSchema = { + channel_id: String, // Snowflake + $suppress: Boolean, + $request_to_speak_timestamp: String // ISO8601 timestamp +}; + +export interface VoiceStateUpdateSchema { + channel_id: string; // Snowflake + suppress?: boolean; + request_to_speak_timestamp?: string // ISO8601 timestamp +} diff --git a/api/src/util/VoiceState.ts b/api/src/util/VoiceState.ts new file mode 100644 index 00000000..07022ec9 --- /dev/null +++ b/api/src/util/VoiceState.ts @@ -0,0 +1,54 @@ +import { Channel, ChannelType, DiscordApiErrors, emitEvent, getPermission, VoiceState, VoiceStateUpdateEvent } from "@fosscord/util"; +import { VoiceStateUpdateSchema } from "../schema"; + + +//TODO need more testing when community guild and voice stage channel are working +export async function updateVoiceState(vsuSchema: VoiceStateUpdateSchema, guildId: string, userId: string, targetUserId?: string) { + const perms = await getPermission(userId, guildId, vsuSchema.channel_id); + + /* + From https://discord.com/developers/docs/resources/guild#modify-current-user-voice-state + You must have the MUTE_MEMBERS permission to unsuppress yourself. You can always suppress yourself. + You must have the REQUEST_TO_SPEAK permission to request to speak. You can always clear your own request to speak. + */ + if (targetUserId !== undefined || (vsuSchema.suppress !== undefined && !vsuSchema.suppress)) { + perms.hasThrow("MUTE_MEMBERS"); + } + if (vsuSchema.request_to_speak_timestamp !== undefined && vsuSchema.request_to_speak_timestamp !== "") { + perms.hasThrow("REQUEST_TO_SPEAK") + } + + if (!targetUserId) { + targetUserId = userId; + } else { + if (vsuSchema.suppress !== undefined && vsuSchema.suppress) + vsuSchema.request_to_speak_timestamp = "" //Need to check if empty string is the right value + } + + //TODO assumed that empty string means clean, need to test if it's right + let voiceState + try { + voiceState = await VoiceState.findOneOrFail({ + guild_id: guildId, + channel_id: vsuSchema.channel_id, + user_id: targetUserId + }); + } catch (error) { + throw DiscordApiErrors.UNKNOWN_VOICE_STATE; + } + + voiceState.assign(vsuSchema); + const channel = await Channel.findOneOrFail({ guild_id: guildId, id: vsuSchema.channel_id }) + if (channel.type !== ChannelType.GUILD_STAGE_VOICE) { + throw DiscordApiErrors.CANNOT_EXECUTE_ON_THIS_CHANNEL_TYPE; + } + + await Promise.all([ + voiceState.save(), + emitEvent({ + event: "VOICE_STATE_UPDATE", + data: voiceState, + guild_id: guildId + } as VoiceStateUpdateEvent)]); + return; +} \ No newline at end of file -- cgit 1.4.1