mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-11 05:02:37 +01:00
Merge pull request #320 from AlTech98/master
Added /guilds/:id/voice-states/ apis, VOICE_SERVER_UPDATE fix
This commit is contained in:
commit
bfdd9c10d8
@ -17,11 +17,15 @@ export function isTextChannel(type: ChannelType): boolean {
|
||||
switch (type) {
|
||||
case ChannelType.GUILD_STORE:
|
||||
case ChannelType.GUILD_VOICE:
|
||||
case ChannelType.GUILD_STAGE_VOICE:
|
||||
case ChannelType.GUILD_CATEGORY:
|
||||
throw new HTTPError("not a text channel", 400);
|
||||
case ChannelType.DM:
|
||||
case ChannelType.GROUP_DM:
|
||||
case ChannelType.GUILD_NEWS:
|
||||
case ChannelType.GUILD_NEWS_THREAD:
|
||||
case ChannelType.GUILD_PUBLIC_THREAD:
|
||||
case ChannelType.GUILD_PRIVATE_THREAD:
|
||||
case ChannelType.GUILD_TEXT:
|
||||
return true;
|
||||
}
|
||||
|
@ -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;
|
15
api/src/routes/guilds/#guild_id/voice-states/@me/index.ts
Normal file
15
api/src/routes/guilds/#guild_id/voice-states/@me/index.ts
Normal file
@ -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;
|
@ -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
|
||||
}
|
||||
|
54
api/src/util/VoiceState.ts
Normal file
54
api/src/util/VoiceState.ts
Normal file
@ -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;
|
||||
}
|
@ -2,7 +2,7 @@ import { VoiceStateUpdateSchema } from "../schema/VoiceStateUpdateSchema";
|
||||
import { Payload } from "../util/Constants";
|
||||
import WebSocket from "../util/WebSocket";
|
||||
import { check } from "./instanceOf";
|
||||
import { Config, emitEvent, Member, VoiceServerUpdateEvent, VoiceState, VoiceStateUpdateEvent } from "@fosscord/util";
|
||||
import { Config, emitEvent, Guild, Member, Region, VoiceServerUpdateEvent, VoiceState, VoiceStateUpdateEvent } from "@fosscord/util";
|
||||
import { genVoiceToken } from "../util/SessionUtils";
|
||||
// TODO: check if a voice server is setup
|
||||
// Notice: Bot users respect the voice channel's user limit, if set. When the voice channel is full, you will not receive the Voice State Update or Voice Server Update events in response to your own Voice State Update. Having MANAGE_CHANNELS permission bypasses this limit and allows you to join regardless of the channel being full or not.
|
||||
@ -11,7 +11,7 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) {
|
||||
check.call(this, VoiceStateUpdateSchema, data.d);
|
||||
const body = data.d as VoiceStateUpdateSchema;
|
||||
|
||||
let voiceState;
|
||||
let voiceState: VoiceState;
|
||||
try {
|
||||
voiceState = await VoiceState.findOneOrFail({
|
||||
where: { user_id: this.user_id }
|
||||
@ -69,14 +69,21 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) {
|
||||
|
||||
//If it's null it means that we are leaving the channel and this event is not needed
|
||||
if (voiceState.channel_id !== null) {
|
||||
const guild = await Guild.findOne({ id: voiceState.guild_id })
|
||||
const regions = Config.get().regions;
|
||||
let guildRegion: Region;
|
||||
if (guild && guild.region) {
|
||||
guildRegion = regions.available.filter(r => (r.id === guild.region))[0]
|
||||
} else {
|
||||
guildRegion = regions.available.filter(r => (r.id === regions.default))[0]
|
||||
}
|
||||
|
||||
await emitEvent({
|
||||
event: "VOICE_SERVER_UPDATE",
|
||||
data: {
|
||||
token: voiceState.token,
|
||||
guild_id: voiceState.guild_id,
|
||||
endpoint: regions.available[0].endpoint, //TODO return best endpoint or default
|
||||
endpoint: guildRegion.endpoint,
|
||||
},
|
||||
guild_id: voiceState.guild_id,
|
||||
} as VoiceServerUpdateEvent);
|
||||
|
@ -16,6 +16,11 @@ export enum ChannelType {
|
||||
GUILD_CATEGORY = 4, // an organizational category that contains up to 50 channels
|
||||
GUILD_NEWS = 5, // a channel that users can follow and crosspost into their own server
|
||||
GUILD_STORE = 6, // a channel in which game developers can sell their game on Discord
|
||||
// TODO: what are channel types between 7-9?
|
||||
GUILD_NEWS_THREAD = 10, // a temporary sub-channel within a GUILD_NEWS channel
|
||||
GUILD_PUBLIC_THREAD = 11, // a temporary sub-channel within a GUILD_TEXT channel
|
||||
GUILD_PRIVATE_THREAD = 12, // a temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission
|
||||
GUILD_STAGE_VOICE = 13, // a voice channel for hosting events with an audience
|
||||
}
|
||||
|
||||
@Entity("channels")
|
||||
|
@ -15,40 +15,9 @@ try {
|
||||
|
||||
export type PermissionResolvable = bigint | number | Permissions | PermissionResolvable[] | PermissionString;
|
||||
|
||||
type PermissionString =
|
||||
| "CREATE_INSTANT_INVITE"
|
||||
| "KICK_MEMBERS"
|
||||
| "BAN_MEMBERS"
|
||||
| "ADMINISTRATOR"
|
||||
| "MANAGE_CHANNELS"
|
||||
| "MANAGE_GUILD"
|
||||
| "ADD_REACTIONS"
|
||||
| "VIEW_AUDIT_LOG"
|
||||
| "PRIORITY_SPEAKER"
|
||||
| "STREAM"
|
||||
| "VIEW_CHANNEL"
|
||||
| "SEND_MESSAGES"
|
||||
| "SEND_TTS_MESSAGES"
|
||||
| "MANAGE_MESSAGES"
|
||||
| "EMBED_LINKS"
|
||||
| "ATTACH_FILES"
|
||||
| "READ_MESSAGE_HISTORY"
|
||||
| "MENTION_EVERYONE"
|
||||
| "USE_EXTERNAL_EMOJIS"
|
||||
| "VIEW_GUILD_INSIGHTS"
|
||||
| "CONNECT"
|
||||
| "SPEAK"
|
||||
| "MUTE_MEMBERS"
|
||||
| "DEAFEN_MEMBERS"
|
||||
| "MOVE_MEMBERS"
|
||||
| "USE_VAD"
|
||||
| "CHANGE_NICKNAME"
|
||||
| "MANAGE_NICKNAMES"
|
||||
| "MANAGE_ROLES"
|
||||
| "MANAGE_WEBHOOKS"
|
||||
| "MANAGE_EMOJIS_AND_STICKERS";
|
||||
type PermissionString = keyof typeof Permissions.FLAGS;
|
||||
|
||||
const CUSTOM_PERMISSION_OFFSET = BigInt(1) << BigInt(48); // 16 free custom permission bits, and 16 for discord to add new ones
|
||||
const CUSTOM_PERMISSION_OFFSET = BigInt(1) << BigInt(48); // 16 free custom permission bits, and 11 for discord to add new ones
|
||||
|
||||
export class Permissions extends BitField {
|
||||
cache: PermissionCache = {};
|
||||
@ -85,6 +54,14 @@ export class Permissions extends BitField {
|
||||
MANAGE_ROLES: BigInt(1) << BigInt(28),
|
||||
MANAGE_WEBHOOKS: BigInt(1) << BigInt(29),
|
||||
MANAGE_EMOJIS_AND_STICKERS: BigInt(1) << BigInt(30),
|
||||
USE_APPLICATION_COMMANDS: BigInt(1) << BigInt(31),
|
||||
REQUEST_TO_SPEAK: BigInt(1) << BigInt(32),
|
||||
// TODO: what is permission 33?
|
||||
MANAGE_THREADS: BigInt(1) << BigInt(34),
|
||||
USE_PUBLIC_THREADS: BigInt(1) << BigInt(35),
|
||||
USE_PRIVATE_THREADS: BigInt(1) << BigInt(36),
|
||||
USE_EXTERNAL_STICKERS: BigInt(1) << BigInt(37),
|
||||
|
||||
/**
|
||||
* CUSTOM PERMISSIONS ideas:
|
||||
* - allow user to dm members
|
||||
|
Loading…
Reference in New Issue
Block a user