feat: add tts commands
This commit is contained in:
@@ -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;
|
||||||
Reference in New Issue
Block a user