Compare commits
9 Commits
83569a27e7
...
07da7538ba
| Author | SHA1 | Date | |
|---|---|---|---|
| 07da7538ba | |||
| 889b86b019 | |||
| cfae674cb4 | |||
| 7d0b5dc459 | |||
| af7c25e6ec | |||
| 9b4d1cbe24 | |||
| aba00e3599 | |||
| 5f1a32dcb3 | |||
| 2ec7212cd3 |
@@ -1,4 +1,5 @@
|
|||||||
node_modules/*
|
node_modules/*
|
||||||
dist/*
|
dist/*
|
||||||
|
|
||||||
|
db.sqlite
|
||||||
.env
|
.env
|
||||||
Generated
+1339
-4
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -42,6 +42,8 @@
|
|||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"node-audio-mixer": "^2.1.0",
|
"node-audio-mixer": "^2.1.0",
|
||||||
"prettier": "^3.7.4",
|
"prettier": "^3.7.4",
|
||||||
"prism-media": "^1.3.5"
|
"prism-media": "^1.3.5",
|
||||||
|
"sequelize": "^6.37.7",
|
||||||
|
"sqlite3": "^5.1.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
import { Logger } from './utils/log';
|
import { Logger } from './utils/log';
|
||||||
import { config } from './utils/config';
|
import { config } from './utils/config';
|
||||||
import { CommandManager } from './commands';
|
import { CommandManager } from './commands';
|
||||||
|
import { DatabaseManager } from './modules/db';
|
||||||
|
|
||||||
type BotEventListeners = {
|
type BotEventListeners = {
|
||||||
messageCreate: (message: Message) => void;
|
messageCreate: (message: Message) => void;
|
||||||
@@ -46,6 +47,12 @@ export class Bot {
|
|||||||
this.log.info('Loading commands');
|
this.log.info('Loading commands');
|
||||||
await this.cmdMgr.init();
|
await this.cmdMgr.init();
|
||||||
|
|
||||||
|
this.log.info('Configuring database');
|
||||||
|
DatabaseManager.get.init(
|
||||||
|
this.cmdMgr.getUserKeys(),
|
||||||
|
this.cmdMgr.getGuildKeys()
|
||||||
|
);
|
||||||
|
|
||||||
this.log.info('Instantiating client');
|
this.log.info('Instantiating client');
|
||||||
this.client = new Client({
|
this.client = new Client({
|
||||||
intents: [
|
intents: [
|
||||||
|
|||||||
+21
-1
@@ -22,8 +22,13 @@ import { isModule } from './utils/misc';
|
|||||||
|
|
||||||
export interface Command {
|
export interface Command {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
builder?: SlashCommandOptionsOnlyBuilder;
|
||||||
requiresAdmin?: boolean;
|
requiresAdmin?: boolean;
|
||||||
ownerOnly?: boolean;
|
ownerOnly?: boolean;
|
||||||
|
|
||||||
|
guild_keys?: Record<string, unknown>;
|
||||||
|
user_keys?: Record<string, unknown>;
|
||||||
|
|
||||||
execute?: (interaction: ChatInputCommandInteraction) => Promise<void>;
|
execute?: (interaction: ChatInputCommandInteraction) => Promise<void>;
|
||||||
autocomplete?: (interaction: AutocompleteInteraction) => Promise<void>;
|
autocomplete?: (interaction: AutocompleteInteraction) => Promise<void>;
|
||||||
messageListener?: (msg: Message) => Promise<void>;
|
messageListener?: (msg: Message) => Promise<void>;
|
||||||
@@ -31,7 +36,6 @@ export interface Command {
|
|||||||
prevState: VoiceState,
|
prevState: VoiceState,
|
||||||
newState: VoiceState
|
newState: VoiceState
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
builder?: SlashCommandOptionsOnlyBuilder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommandCategoryInfo {
|
export interface CommandCategoryInfo {
|
||||||
@@ -95,6 +99,22 @@ export class CommandManager {
|
|||||||
return this.getAll().filter((cmd) => !!cmd.name);
|
return this.getAll().filter((cmd) => !!cmd.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getGuildKeys(): Record<string, unknown> {
|
||||||
|
return this.mergeKeys('guild_keys');
|
||||||
|
}
|
||||||
|
|
||||||
|
public getUserKeys(): Record<string, unknown> {
|
||||||
|
return this.mergeKeys('user_keys');
|
||||||
|
}
|
||||||
|
|
||||||
|
private mergeKeys(
|
||||||
|
keyType: 'guild_keys' | 'user_keys'
|
||||||
|
): Record<string, unknown> {
|
||||||
|
return this.getAll()
|
||||||
|
.flatMap((cmd) => (cmd[keyType] ? [cmd[keyType]] : []))
|
||||||
|
.reduce((acc, val) => Object.assign(acc, val), {});
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
internal
|
internal
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { CommandCategoryInfo } from '../../commands';
|
||||||
|
|
||||||
|
const info: CommandCategoryInfo = {
|
||||||
|
name: 'TTS',
|
||||||
|
description: 'Text to Speech related commands'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default info;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ChatInputCommandInteraction, SlashCommandBuilder } from 'discord.js';
|
||||||
|
import { Command } from '../../commands';
|
||||||
|
|
||||||
|
const builder = new SlashCommandBuilder()
|
||||||
|
.setName('tts-clear')
|
||||||
|
.setDescription('Clears the TTS queue');
|
||||||
|
|
||||||
|
const cmd: Command = {
|
||||||
|
name: builder.name,
|
||||||
|
builder: builder,
|
||||||
|
requiresAdmin: true,
|
||||||
|
execute: async (interaction: ChatInputCommandInteraction): Promise<void> => {
|
||||||
|
if (!interaction.guildId) {
|
||||||
|
interaction.reply('This command only works on Guilds');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
interaction.reply('Queue cleared.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default cmd;
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import { Message } from 'discord.js';
|
||||||
|
import { Command } from '../../commands';
|
||||||
|
import { Logger } from '../../utils/log';
|
||||||
|
import { getVoiceConnection, VoiceConnectionStatus } from '@discordjs/voice';
|
||||||
|
import { TTSManager } from '../../modules/tts';
|
||||||
|
import { AudioStreamManager } from '../../modules/audiostreams';
|
||||||
|
import { Readable } from 'stream';
|
||||||
|
import { DataTypes } from 'sequelize';
|
||||||
|
import { config } from '../../utils/config';
|
||||||
|
import { DatabaseManager } from '../../modules/db';
|
||||||
|
|
||||||
|
const URL_REGEX = /(?:https?|ftp):\/\/[\n\S]+/g;
|
||||||
|
|
||||||
|
class TTSListener implements Command {
|
||||||
|
private log: Logger;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.log = new Logger('TTS Listener');
|
||||||
|
}
|
||||||
|
|
||||||
|
user_keys = {
|
||||||
|
tts_voice: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
defaultValue: config.tts_default_voice
|
||||||
|
},
|
||||||
|
tts_mode: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
defaultValue: config.tts_default_mode
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
guild_keys = {
|
||||||
|
tts_channel: {
|
||||||
|
type: DataTypes.STRING
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
messageListener = async (msg: Message): Promise<void> => {
|
||||||
|
if (msg.content.length === 0) return;
|
||||||
|
|
||||||
|
const guild = msg.guild;
|
||||||
|
if (!guild) return;
|
||||||
|
|
||||||
|
const me = guild.members.me;
|
||||||
|
if (!me || !me.voice) return;
|
||||||
|
|
||||||
|
const member = msg.member;
|
||||||
|
if (!member || !member.voice) return;
|
||||||
|
|
||||||
|
if (member.voice.channelId !== me.voice.channelId) return;
|
||||||
|
|
||||||
|
const voiceConnection = getVoiceConnection(guild.id);
|
||||||
|
if (
|
||||||
|
!voiceConnection ||
|
||||||
|
voiceConnection.state.status !== VoiceConnectionStatus.Ready
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const db = await DatabaseManager.get;
|
||||||
|
|
||||||
|
const guildData = await db.getGuild(guild.id);
|
||||||
|
if (msg.channelId !== guildData.get('tts_channel')) return;
|
||||||
|
|
||||||
|
const userData = await db.getUser(member.id);
|
||||||
|
|
||||||
|
const modName = userData.get('tts_mode') as string;
|
||||||
|
const voiceName = userData.get('tts_voice') as string;
|
||||||
|
|
||||||
|
const processTTS = async () => {
|
||||||
|
try {
|
||||||
|
const ttsModule = TTSManager.get.getModule(modName);
|
||||||
|
if (!ttsModule) return;
|
||||||
|
|
||||||
|
const voices = await ttsModule.getVoices();
|
||||||
|
if (!voices) return;
|
||||||
|
if (!voices.includes(voiceName)) return;
|
||||||
|
|
||||||
|
const msgFiltered = msg.content.replace(URL_REGEX, 'a link');
|
||||||
|
|
||||||
|
const audio = await ttsModule.generate(voiceName, msgFiltered);
|
||||||
|
|
||||||
|
if (audio?.data) {
|
||||||
|
const stream =
|
||||||
|
AudioStreamManager.get.getOrCreateStream(voiceConnection);
|
||||||
|
const queue = stream.getQueue('TTS');
|
||||||
|
queue.enqueue(Readable.from(audio.data));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.log.error('error occurred while processing TTS message (%s)', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
processTTS();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new TTSListener();
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import {
|
||||||
|
ChatInputCommandInteraction,
|
||||||
|
MessageFlags,
|
||||||
|
SlashCommandBuilder
|
||||||
|
} from 'discord.js';
|
||||||
|
import { Command } from '../../commands';
|
||||||
|
import { DatabaseManager } from '../../modules/db';
|
||||||
|
|
||||||
|
const builder = new SlashCommandBuilder()
|
||||||
|
.setName('tts-channel')
|
||||||
|
.setDescription('Sets the channel where TTS messages will be read from');
|
||||||
|
|
||||||
|
const cmd: Command = {
|
||||||
|
name: builder.name,
|
||||||
|
builder: builder,
|
||||||
|
|
||||||
|
execute: async (interaction: ChatInputCommandInteraction): Promise<void> => {
|
||||||
|
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||||
|
|
||||||
|
if (!interaction.guild) {
|
||||||
|
interaction.editReply('This message can only be executed on guilds');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const guildData = await DatabaseManager.get.getGuild(interaction.guild.id);
|
||||||
|
|
||||||
|
await guildData.set('tts_channel', interaction.channelId);
|
||||||
|
await guildData.save();
|
||||||
|
|
||||||
|
interaction.editReply('TTS channel updated.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default cmd;
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import {
|
||||||
|
AutocompleteInteraction,
|
||||||
|
ChatInputCommandInteraction,
|
||||||
|
MessageFlags,
|
||||||
|
SlashCommandBuilder
|
||||||
|
} from 'discord.js';
|
||||||
|
import { Command } from '../../commands';
|
||||||
|
import { DatabaseManager } from '../../modules/db';
|
||||||
|
import { TTSManager } from '../../modules/tts';
|
||||||
|
|
||||||
|
const builder = new SlashCommandBuilder()
|
||||||
|
.setName('tts-mode')
|
||||||
|
.setDescription('Selects a mode to use for TTS')
|
||||||
|
.addStringOption((opt) =>
|
||||||
|
opt
|
||||||
|
.setName('mode')
|
||||||
|
.setDescription('Which TTS provider to use')
|
||||||
|
.setAutocomplete(true)
|
||||||
|
.setRequired(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
const cmd: Command = {
|
||||||
|
name: builder.name,
|
||||||
|
builder: builder,
|
||||||
|
|
||||||
|
execute: async (interaction: ChatInputCommandInteraction): Promise<void> => {
|
||||||
|
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||||
|
|
||||||
|
const userData = await DatabaseManager.get.getUser(interaction.user.id);
|
||||||
|
const modeName = interaction.options.getString('mode', true);
|
||||||
|
const selectedMode = TTSManager.get
|
||||||
|
.getModules()
|
||||||
|
.find((mode) => mode.name === modeName);
|
||||||
|
|
||||||
|
if (!selectedMode) {
|
||||||
|
await interaction.editReply(`Unknown mode (${modeName})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await userData.set('tts_mode', modeName);
|
||||||
|
await userData.save();
|
||||||
|
|
||||||
|
interaction.editReply(`TTS mode has been set to: ${modeName}.`);
|
||||||
|
},
|
||||||
|
|
||||||
|
autocomplete: async (interaction: AutocompleteInteraction): Promise<void> => {
|
||||||
|
const focused = interaction.options.getFocused(true);
|
||||||
|
if (focused.name != 'mode') return;
|
||||||
|
|
||||||
|
const modes = TTSManager.get.getModules();
|
||||||
|
|
||||||
|
const filtered: string[] = modes
|
||||||
|
.filter((mode) =>
|
||||||
|
mode.name.toLowerCase().startsWith(focused.value.toLowerCase())
|
||||||
|
)
|
||||||
|
.map((mode) => mode.name)
|
||||||
|
.slice(0, 25);
|
||||||
|
|
||||||
|
await interaction.respond(
|
||||||
|
filtered.map((choice) => ({ name: choice, value: choice }))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default cmd;
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import {
|
||||||
|
AutocompleteInteraction,
|
||||||
|
ChatInputCommandInteraction,
|
||||||
|
MessageFlags,
|
||||||
|
SlashCommandBuilder
|
||||||
|
} from 'discord.js';
|
||||||
|
import { Command } from '../../commands';
|
||||||
|
import { DatabaseManager } from '../../modules/db';
|
||||||
|
import { TTSManager } from '../../modules/tts';
|
||||||
|
|
||||||
|
const builder = new SlashCommandBuilder()
|
||||||
|
.setName('tts-voice')
|
||||||
|
.setDescription('Selects a voice to use for TTS')
|
||||||
|
.addStringOption((opt) =>
|
||||||
|
opt
|
||||||
|
.setName('voice')
|
||||||
|
.setDescription('Which voice to use')
|
||||||
|
.setAutocomplete(true)
|
||||||
|
.setRequired(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
const cmd: Command = {
|
||||||
|
name: builder.name,
|
||||||
|
builder: builder,
|
||||||
|
|
||||||
|
execute: async (interaction: ChatInputCommandInteraction): Promise<void> => {
|
||||||
|
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
|
||||||
|
|
||||||
|
const userData = await DatabaseManager.get.getUser(interaction.user.id);
|
||||||
|
const mod = TTSManager.get.getModule(userData.get('tts_mode') as string);
|
||||||
|
if (!mod) return;
|
||||||
|
|
||||||
|
const voices = await mod.getVoices();
|
||||||
|
if (!voices) {
|
||||||
|
await interaction.editReply(
|
||||||
|
'Unknown error occured while fetching TTS voices'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const voiceName = interaction.options.getString('voice', true);
|
||||||
|
if (!voices.includes(voiceName)) {
|
||||||
|
await interaction.editReply('Invalid voice');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await userData.set('tts_voice', voiceName);
|
||||||
|
await userData.save();
|
||||||
|
|
||||||
|
interaction.editReply(`TTS voice has been set to: ${voiceName}.`);
|
||||||
|
},
|
||||||
|
autocomplete: async (interaction: AutocompleteInteraction): Promise<void> => {
|
||||||
|
const focused = interaction.options.getFocused(true);
|
||||||
|
if (focused.name != 'voice') return;
|
||||||
|
|
||||||
|
const userData = await DatabaseManager.get.getUser(interaction.user.id);
|
||||||
|
|
||||||
|
const mode = TTSManager.get.getModule(userData.get('tts_mode') as string);
|
||||||
|
if (!mode) return;
|
||||||
|
|
||||||
|
const voices: string[] | undefined = await mode.getVoices();
|
||||||
|
if (!voices) return;
|
||||||
|
|
||||||
|
const filtered: string[] = voices
|
||||||
|
.filter((voice) =>
|
||||||
|
voice.toLowerCase().startsWith(focused.value.toLowerCase())
|
||||||
|
)
|
||||||
|
.slice(0, 25);
|
||||||
|
|
||||||
|
await interaction.respond(
|
||||||
|
filtered.map((choice) => ({ name: choice, value: choice }))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default cmd;
|
||||||
+27
-14
@@ -1,10 +1,19 @@
|
|||||||
import { ChatInputCommandInteraction, GuildMember, SlashCommandBuilder } from "discord.js";
|
import {
|
||||||
import { Command } from "../../commands";
|
ChatInputCommandInteraction,
|
||||||
import { CreateVoiceConnectionOptions, getVoiceConnection, joinVoiceChannel, JoinVoiceChannelOptions } from "@discordjs/voice";
|
GuildMember,
|
||||||
|
SlashCommandBuilder
|
||||||
|
} from 'discord.js';
|
||||||
|
import { Command } from '../../commands';
|
||||||
|
import {
|
||||||
|
CreateVoiceConnectionOptions,
|
||||||
|
getVoiceConnection,
|
||||||
|
joinVoiceChannel,
|
||||||
|
JoinVoiceChannelOptions
|
||||||
|
} from '@discordjs/voice';
|
||||||
|
|
||||||
const builder = new SlashCommandBuilder()
|
const builder = new SlashCommandBuilder()
|
||||||
.setName("join")
|
.setName('join')
|
||||||
.setDescription("Makes the bot join your current voice channel");
|
.setDescription('Makes the bot join your current voice channel');
|
||||||
|
|
||||||
const cmd: Command = {
|
const cmd: Command = {
|
||||||
name: builder.name,
|
name: builder.name,
|
||||||
@@ -12,23 +21,27 @@ const cmd: Command = {
|
|||||||
execute: async (interaction: ChatInputCommandInteraction): Promise<void> => {
|
execute: async (interaction: ChatInputCommandInteraction): Promise<void> => {
|
||||||
const member = interaction.member as GuildMember;
|
const member = interaction.member as GuildMember;
|
||||||
if (!member || !interaction.guild) {
|
if (!member || !interaction.guild) {
|
||||||
interaction.reply("This command only works on guilds");
|
interaction.reply('This command only works on guilds');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!member.voice.channelId) {
|
if (!member.voice.channelId) {
|
||||||
interaction.reply("You are not currently on a voice channel");
|
interaction.reply('You are not currently on a voice channel');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const me = interaction.guild.members.me as GuildMember;
|
const me = interaction.guild.members.me as GuildMember;
|
||||||
|
|
||||||
if (getVoiceConnection(interaction.guild.id) && me.voice.channelId === member.voice.channelId) {
|
if (
|
||||||
interaction.reply("Already connected");
|
getVoiceConnection(interaction.guild.id) &&
|
||||||
|
me.voice.channelId === member.voice.channelId
|
||||||
|
) {
|
||||||
|
interaction.reply('Already connected');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const voiceOptions: JoinVoiceChannelOptions & CreateVoiceConnectionOptions = {
|
const voiceOptions: JoinVoiceChannelOptions & CreateVoiceConnectionOptions =
|
||||||
|
{
|
||||||
channelId: member.voice.channelId,
|
channelId: member.voice.channelId,
|
||||||
guildId: interaction.guild.id,
|
guildId: interaction.guild.id,
|
||||||
adapterCreator: interaction.guild.voiceAdapterCreator
|
adapterCreator: interaction.guild.voiceAdapterCreator
|
||||||
@@ -36,12 +49,12 @@ const cmd: Command = {
|
|||||||
|
|
||||||
const connection = await joinVoiceChannel(voiceOptions);
|
const connection = await joinVoiceChannel(voiceOptions);
|
||||||
if (!connection) {
|
if (!connection) {
|
||||||
interaction.reply("Unable to join");
|
interaction.reply('Unable to join');
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
interaction.reply("Joined");
|
interaction.reply('Joined');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default cmd;
|
export default cmd;
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
import { ChatInputCommandInteraction, GuildMember, SlashCommandBuilder } from "discord.js";
|
import {
|
||||||
import { Command } from "../../commands";
|
ChatInputCommandInteraction,
|
||||||
import { getVoiceConnection } from "@discordjs/voice";
|
GuildMember,
|
||||||
|
SlashCommandBuilder
|
||||||
|
} from 'discord.js';
|
||||||
|
import { Command } from '../../commands';
|
||||||
|
import { getVoiceConnection } from '@discordjs/voice';
|
||||||
|
|
||||||
const builder = new SlashCommandBuilder()
|
const builder = new SlashCommandBuilder()
|
||||||
.setName("leave")
|
.setName('leave')
|
||||||
.setDescription("Makes the bot leave its current voice channel");
|
.setDescription('Makes the bot leave its current voice channel');
|
||||||
|
|
||||||
const cmd: Command = {
|
const cmd: Command = {
|
||||||
name: builder.name,
|
name: builder.name,
|
||||||
@@ -12,21 +16,21 @@ const cmd: Command = {
|
|||||||
execute: async (interaction: ChatInputCommandInteraction): Promise<void> => {
|
execute: async (interaction: ChatInputCommandInteraction): Promise<void> => {
|
||||||
const member = interaction.member as GuildMember;
|
const member = interaction.member as GuildMember;
|
||||||
if (!member || interaction.guild === null) {
|
if (!member || interaction.guild === null) {
|
||||||
interaction.reply("This command only works on guilds");
|
interaction.reply('This command only works on guilds');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const connection = getVoiceConnection(interaction.guildId as string);
|
const connection = getVoiceConnection(interaction.guildId as string);
|
||||||
if (!connection) {
|
if (!connection) {
|
||||||
interaction.reply('currently not connected to a voice channel')
|
interaction.reply('currently not connected to a voice channel');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
connection.destroy();
|
connection.destroy();
|
||||||
|
|
||||||
interaction.reply("Disconnected");
|
interaction.reply('Disconnected');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default cmd;
|
export default cmd;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { VoiceState } from "discord.js";
|
import { VoiceState } from 'discord.js';
|
||||||
import { Command } from "../../commands";
|
import { Command } from '../../commands';
|
||||||
import { getVoiceConnection } from "@discordjs/voice";
|
import { getVoiceConnection } from '@discordjs/voice';
|
||||||
|
|
||||||
const cmd: Command = {
|
const cmd: Command = {
|
||||||
voiceStateListener: async function (oldState: VoiceState): Promise<void> {
|
voiceStateListener: async function (oldState: VoiceState): Promise<void> {
|
||||||
@@ -11,18 +11,15 @@ const cmd: Command = {
|
|||||||
if (!voiceConnection) return;
|
if (!voiceConnection) return;
|
||||||
|
|
||||||
const me = guild.members.me;
|
const me = guild.members.me;
|
||||||
if (!me)
|
if (!me) return;
|
||||||
return;
|
|
||||||
|
|
||||||
if (!me.voice.channel)
|
if (!me.voice.channel) return;
|
||||||
return;
|
|
||||||
|
|
||||||
if (me.voice.channel.members.size > 1)
|
if (me.voice.channel.members.size > 1) return;
|
||||||
return;
|
|
||||||
|
|
||||||
voiceConnection.disconnect();
|
voiceConnection.disconnect();
|
||||||
voiceConnection.destroy();
|
voiceConnection.destroy();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export default cmd;
|
export default cmd;
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import {
|
||||||
|
DataTypes,
|
||||||
|
Model,
|
||||||
|
ModelStatic,
|
||||||
|
Sequelize
|
||||||
|
} from 'sequelize';
|
||||||
|
|
||||||
|
export class DatabaseManager {
|
||||||
|
private readonly db: Sequelize;
|
||||||
|
|
||||||
|
public guildData: ModelStatic<Model> | null = null;
|
||||||
|
public userData: ModelStatic<Model> | null = null;
|
||||||
|
|
||||||
|
private constructor() {
|
||||||
|
this.db = new Sequelize('db', 'luma', '', {
|
||||||
|
host: 'localhost',
|
||||||
|
dialect: 'sqlite',
|
||||||
|
storage: 'db.sqlite',
|
||||||
|
logging: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.db.sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async init(userKeys: object, guildKeys: object) {
|
||||||
|
this.userData = this.db.define('user_data', {
|
||||||
|
user_id: { type: DataTypes.STRING, unique: true, primaryKey: true },
|
||||||
|
...userKeys
|
||||||
|
});
|
||||||
|
|
||||||
|
this.guildData = this.db.define('guild_data', {
|
||||||
|
guild_id: { type: DataTypes.STRING, unique: true, primaryKey: true },
|
||||||
|
...guildKeys
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.db.sync({ alter: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
public sync(): void {
|
||||||
|
this.db.sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getUser(userId: string) {
|
||||||
|
if (!this.userData) throw new Error('Database not initialized');
|
||||||
|
|
||||||
|
const [user] = await this.userData.findOrCreate({
|
||||||
|
where: { user_id: userId },
|
||||||
|
defaults: { user_id: userId }
|
||||||
|
});
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getGuild(guildId: string) {
|
||||||
|
if (!this.guildData) throw new Error('Database not initialized');
|
||||||
|
|
||||||
|
const [guild] = await this.guildData.findOrCreate({
|
||||||
|
where: { guild_id: guildId },
|
||||||
|
defaults: { guild_id: guildId }
|
||||||
|
});
|
||||||
|
return guild;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
singleton logic
|
||||||
|
*/
|
||||||
|
static #instance: DatabaseManager | null = null;
|
||||||
|
|
||||||
|
public static get get(): DatabaseManager {
|
||||||
|
if (!DatabaseManager.#instance)
|
||||||
|
DatabaseManager.#instance = new DatabaseManager();
|
||||||
|
return DatabaseManager.#instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user