-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.ts
More file actions
115 lines (93 loc) · 3.43 KB
/
app.ts
File metadata and controls
115 lines (93 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import {
Client,
GatewayIntentBits,
VoiceState,
ChannelType,
PermissionFlagsBits,
VoiceBasedChannel,
GuildMember
} from 'discord.js';
import dotenv from 'dotenv';
dotenv.config();
const channelMarkers = {
JOIN_TO_CONNECT: "PrioritySpeaker",
TEMPORARY: "RequestToSpeak"
} as const
const requiredPermissions = [
PermissionFlagsBits.ViewChannel,
PermissionFlagsBits.Connect,
PermissionFlagsBits.ManageChannels,
PermissionFlagsBits.ManageRoles,
PermissionFlagsBits.MoveMembers,
PermissionFlagsBits[channelMarkers.TEMPORARY]
]
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildVoiceStates
]
});
client.on('voiceStateUpdate', async (oldState, newState) => {
if (oldState.channelId === newState.channelId) return;
if (oldState.channel) await handleLeave(oldState);
if (newState.channel) await handleJoin(newState);
});
async function handleJoin(state: VoiceState): Promise<void> {
const { channel, member, guild } = state;
if (!channel || !member || !guild) return;
if (!isJTCChannel(channel)) return;
const newChannel = await guild.channels.create({
name: `${member.user.displayName}'s Channel`,
type: ChannelType.GuildVoice,
parent: channel.parent?.id
});
await newChannel.permissionOverwrites.edit(
client.user!.id,
{
[channelMarkers.TEMPORARY]: true
}
);
await setChannelOwner(newChannel, member);
await member.voice.setChannel(newChannel);
}
async function handleLeave(state: VoiceState): Promise<void> {
const { channel, member } = state;
if (!channel || !member) return;
if (!isTemporaryChannel(channel)) return;
if (channel.members.size === 0) {
await channel.delete();
} else {
await channel.permissionOverwrites.delete(member.id);
const nextOwner = channel.members.first();
if (nextOwner) await setChannelOwner(channel, nextOwner);
}
}
function isJTCChannel(channel: VoiceBasedChannel): boolean {
const category = channel.parent;
if (!category) return false;
const categoryPerms = category.permissionsFor(client.user!);
if (!categoryPerms?.has(requiredPermissions)) return false;
const channelPerms = channel.permissionOverwrites.cache.get(channel.client.user!.id);
return channelPerms?.allow.has(PermissionFlagsBits[channelMarkers.JOIN_TO_CONNECT]) ?? false;
}
function isTemporaryChannel(channel: VoiceBasedChannel): boolean {
const category = channel.parent;
if (!category) return false;
const jtcChannel = category.children.cache
.filter(ch => ch.type === ChannelType.GuildVoice)
.find(ch => isJTCChannel(ch as VoiceBasedChannel));
if (!jtcChannel) return false;
const channelPerms = channel.permissionOverwrites.cache.get(channel.client.user!.id);
return (
channelPerms?.allow.has(PermissionFlagsBits[channelMarkers.TEMPORARY]) &&
!channelPerms?.allow.has(PermissionFlagsBits[channelMarkers.JOIN_TO_CONNECT])
) ?? false;
}
async function setChannelOwner(channel: VoiceBasedChannel, member: GuildMember) {
await channel.permissionOverwrites.edit(member, {
Connect: true,
ManageChannels: true,
MoveMembers: true,
});
}
client.login(process.env.DISCORD_TOKEN);