discord-tempvoice/index.js

324 lines
11 KiB
JavaScript

const config = require("./config.json");
const Discord = require("discord.js");
const {
REST,
Routes
} = require('discord.js');
const rest = new REST({
version: '10'
}).setToken(config.discord.token);
const fs = require("fs");
const path = require("path");
const colors = require("colors");
const sqlite3 = require("sqlite3").verbose();
const db = new sqlite3.Database("./database.db");
// If database table doesnt exist, create it
// two tables, one for per-guild settings, one for keeping track of temp voice channels
// guild_settings: guild_id text, voice_category_id text, creation_channel_id text, max_temp_channels integer, max_per_user integer
// temp_channels: channel_id text, guild_id text, creator_id text
db.run(`CREATE TABLE IF NOT EXISTS guild_settings (
voice_category_id TEXT PRIMARY KEY,
guild_id TEXT,
creation_channel_id TEXT
)`);
db.run(`CREATE TABLE IF NOT EXISTS temp_channels (
channel_id TEXT PRIMARY KEY,
guild_id TEXT,
creator_id TEXT
)`);
const client = new Discord.Client({
intents: [
"Guilds",
"GuildVoiceStates"
]
});
client.on("ready", async () => {
console.log(`${colors.cyan("[INFO]")} Logged in as ${colors.green(client.user.tag)}`);
const commands = [
{
name: "setup",
description: "Setup the bot for your server",
default_member_permissions: 16,
options: [
{
name: "category",
description: "The category to create the temporary voice channels in",
type: 7,
required: true
}
]
}
]
console.log(`${colors.cyan("[INFO]")} Started refreshing application (/) commands.`);
await rest.put(
Routes.applicationCommands(client.user.id), {
body: commands
}
);
console.log(`${colors.cyan("[INFO]")} Successfully reloaded application (/) commands.`);
console.log(`${colors.cyan("[INFO]")} Checking for empty temporary voice channels and missing creation channels.`);
deletedCreation = 0
// Run through every category that is set up in the database and make sure the creation channel exists, if not, remove the entry from the database. Dont attempt to make a new channel, its likely they wanted to delete it
await db.all("SELECT * FROM guild_settings", (err, rows) => {
if (err) {
console.error(err);
}
rows.forEach((row) => {
const guild = client.guilds.cache.get(row.guild_id);
const voice_category = guild.channels.cache.get(row.voice_category_id);
const creation_channel = guild.channels.cache.get(row.creation_channel_id);
if (!creation_channel) {
deletedCreation++
db.run("DELETE FROM guild_settings WHERE voice_category_id = ?", row.voice_category_id, (err) => {
if (err) {
console.error(err);
}
});
}
});
});
deletedTemp = 0;
// Run through every temp channel in the database, if it's empty delete it
await db.all("SELECT * FROM temp_channels", (err, rows) => {
if (err) {
console.error(err);
}
rows.forEach((row) => {
const guild = client.guilds.cache.get(row.guild_id);
const channel = guild.channels.cache.get(row.channel_id);
if (!channel.members || channel.members.size === 0) {
deletedTemp+
channel.delete();
db.run("DELETE FROM temp_channels WHERE channel_id = ?", row.channel_id, (err) => {
if (err) {
console.error(err);
}
});
}
});
});
console.log(`${colors.cyan("[INFO]")} Checked for empty temporary voice channels and missing creation channels. Deleted ${deletedCreation} creation channels and ${deletedTemp} temporary voice channels.`);
})
client.on("interactionCreate", async (interaction) => {
if (!interaction.isCommand()) return;
const command = interaction.commandName;
switch (command) {
case "setup": // Initial and post-setup configuration
// Check if the channel is already set up, if so and no settings are provided, tell the user to specify settings to update, use channel id, not guild.
// If the channel is not set up, set it up with the provided settings or default settings
const guild_id = interaction.guild.id;
const voice_category_id = interaction.options.getChannel("category");
// Check that voice_category_id is a category
if (voice_category_id.type !== Discord.ChannelType.GuildCategory) {
return interaction.reply({
content: "The channel you selected is not a category! Please select a category to set up the temporary voice channels in.",
ephemeral: true
})
}
db.get("SELECT * FROM guild_settings WHERE voice_category_id = ?", voice_category_id.id, (err, row) => {
if (err) {
console.error(err);
interaction.reply({
content: "An error occurred while setting up the channel. Please try again later.",
ephemeral: true
});
}
if (row) { // Channel already set up, update settings
// get channel info
const voice_category = interaction.guild.channels.cache.get(row.voice_category_id);
// Do a quick check to see if the creation channel still exists, if not, create and update the field for it
if (!interaction.guild.channels.cache.get(row.creation_channel_id)) {
interaction.guild.channels.create({
name: "Create a Channel",
type: Discord.ChannelType.GuildVoice,
parent: voice_category_id.id
}).then((channel) => {
db.run("UPDATE guild_settings SET creation_channel_id = ? WHERE voice_category_id = ?", channel.id, voice_category_id.id, (err) => {
if (err) {
console.error(err);
interaction.reply({
content: "An error occurred while updating the channel settings. Please try again later.",
ephemeral: true
});
}
return interaction.reply({
content: "Successfully updated the temporary voice channels.",
ephemeral: true
});
});
}).catch((err) => {
console.error(err);
interaction.reply({
content: "An error occurred while updating the channel settings. Please try again later.",
ephemeral: true
});
});
}
return interaction.reply({
content: `The temporary voice channels are already set up in ${voice_category.name}.`,
ephemeral: true
});
} else {
// Channel not set up, set up channel (make a voice channel in the category called "Create a Channel", then set up the settings)
// Create the channel
interaction.guild.channels.create({
name: "Create a Channel",
type: Discord.ChannelType.GuildVoice,
parent: voice_category_id.id
}).then((channel) => {
// Set up the settings
db.run("INSERT INTO guild_settings (voice_category_id, guild_id, creation_channel_id) VALUES (?, ?, ?)", voice_category_id.id, guild_id, channel.id, (err) => {
if (err) {
console.error(err);
interaction.reply({
content: "An error occurred while setting up the channel. Please try again later.",
ephemeral: true
});
}
interaction.reply({
content: `Successfully set up the temporary voice channels in ${voice_category_id.name}.`,
ephemeral: true
});
});
}).catch((err) => {
console.error(err);
interaction.reply({
content: "An error occurred while setting up the channel. Please try again later.",
ephemeral: true
});
});
}
});
break;
}
});
client.on("voiceStateUpdate", async (oldState, newState) => {
// Handle creation
// Check if the user is in a creation channel, if so make a new channel, name format is temp #<tempchannel count for guild>, then move them there
if (newState.channel) {
db.get("SELECT * FROM guild_settings WHERE creation_channel_id = ?", newState.channel.id, (err, row) => {
if (err) {
console.error(err);
}
if (row) {
// User is in a creation channel
const guild = newState.guild.id;
const creator = newState.member.id;
// Get the number of temp channels in a guild
db.get("SELECT COUNT(*) FROM temp_channels WHERE guild_id = ?", guild, (err, row2) => {
if (err) {
console.error(err);
}
const temp_channel_count = row2["COUNT(*)"];
// Create a new channel
newState.guild.channels.create({
name: `Temp #${temp_channel_count + 1}`,
type: Discord.ChannelType.GuildVoice,
parent: row.voice_category_id
}).then((channel) => {
channel.send({
embeds: [
{
color: 0xff0000,
title: "Temporary Voice Channel",
description: `This is a temporary voice channel created by ${newState.member.user}.\nThis voice channel and the messages within will be deleted when the channel is empty!`,
}
]
});
// Move the user to the new channel
newState.setChannel(channel);
// Update the database
db.run("INSERT INTO temp_channels (channel_id, guild_id, creator_id) VALUES (?, ?, ?)", channel.id, guild, creator, (err) => {
if (err) {
console.error(err);
}
});
}).catch((err) => {
console.error(err);
});
});
}
});
}
// Handle deletion
// Check if the user left a temp channel, if they were the last person in the channel, delete it
if (oldState.channel) {
db.get("SELECT * FROM temp_channels WHERE channel_id = ?", oldState.channel.id, (err, row) => {
if (err) {
console.error(err);
}
if (row) {
// User left a temp channel
const channel = oldState.channel;
// Check if the user was the last person in the channel
if (!channel.members || channel.members.size === 0) {
// Delete the channel
channel.delete();
// Update the database
db.run("DELETE FROM temp_channels WHERE channel_id = ?", channel.id , (err) => {
if (err) {
console.error(err);
}
});
}
}
});
}
});
client.on("channelDelete", async (channel) => {
// Check if the channel deleted was a creation channel, if so, remove the settings from the database
db.get("SELECT * FROM guild_settings WHERE creation_channel_id = ?", channel.id, (err, row) => {
if (err) {
console.error(err);
}
if (row) {
db.run("DELETE FROM guild_settings WHERE creation_channel_id = ?", channel.id, (err) => {
if (err) {
console.error(err);
}
});
}
});
// Check if it was a temp channel, if so, delete it from the database
db.get("SELECT * FROM temp_channels WHERE channel_id = ?", channel.id, (err, row) => {
if (err) {
console.error(err);
}
if (row) {
db.run("DELETE FROM temp_channels WHERE channel_id = ?", channel.id, (err) => {
if (err) {
console.error(err);
}
});
}
});
});
// Lets actually handle exceptions now
process.on('unhandledRejection', (error) => {
// Log a full error with line number
console.log(`${colors.red("[ERROR]")} ${error}`);
});
process.on('uncaughtException', (error) => {
// Log a full error with line number
console.log(`${colors.red("[ERROR]")} ${error}`);
});
client.login(config.discord.token)