Node.js+Discord.js开发Discord机器人:Vosk语音转文本实现问题
Discord Vosk语音转录机器人问题解决方案
一、修复KaldiRecognizer构造错误
出现TypeError: KaldiRecognizer is not a constructor是因为未正确引用Vosk的构造函数,两种修复方式任选其一:
方式1:解构导入Vosk模块
取消注释代码中的解构导入语句,直接引用KaldiRecognizer:
const { Model, KaldiRecognizer } = require('vosk'); // 替换原有的 const vosk = require('vosk');
之后即可正常使用new KaldiRecognizer(model, 16000)。
方式2:通过vosk对象访问构造函数
如果保留const vosk = require('vosk');的导入方式,需通过vosk实例访问构造函数:
const recognizer = new vosk.KaldiRecognizer(model, 16000);
二、正确获取语音频道音频并实现转录
Discord.js Voice的AudioPlayer用于播放音频,接收用户语音需使用VoiceConnection的receiver对象。以下是修正后的完整实现:
修正后的核心代码
替换原有的performSpeechRecognition函数及execute逻辑:
const { SlashCommandBuilder} = require('discord.js'); const { joinVoiceChannel } = require('@discordjs/voice'); const { Model, KaldiRecognizer } = require('vosk'); // 推荐使用绝对路径避免模型路径问题 const model = new Model('./model/vosk-model-en-us-0.42-gigaspeech'); module.exports = { data: new SlashCommandBuilder() .setName('connect') .setDescription('Join channel to start listening'), async execute(interaction) { const transcriptChannel = interaction.guild.channels.cache.find(c => c.name === 'transcript'); const voiceChannel = interaction.member.voice.channel; if (!voiceChannel) { await interaction.reply({content: 'Error: 请先加入语音频道!', ephemeral: true}); return; } if (!transcriptChannel) { await interaction.reply({content: 'Error: 未找到名为"transcript"的文本频道!', ephemeral: true}); return; } const voiceConn = joinVoiceChannel({ channelId: voiceChannel.id, guildId: interaction.guild.id, adapterCreator: interaction.guild.voiceAdapterCreator, selfDeaf: false, // 必须关闭自聋才能接收语音 }); await interaction.reply({ content: '已准备好监听语音!', ephemeral: true }); setupSpeechRecognition(voiceConn, model, transcriptChannel, interaction.guild); } }; function setupSpeechRecognition(voiceConn, model, transcriptChannel, guild) { // 监听用户说话开始事件 voiceConn.receiver.speaking.on('start', (userId) => { const speaker = guild.members.cache.get(userId); if (!speaker) return; // 创建针对当前说话用户的音频流 const audioStream = voiceConn.receiver.subscribe(userId, { end: { behavior: 'manual' } // 手动控制流结束,避免提前断开 }); // 初始化Vosk识别器(16000Hz采样率,单声道PCM) const recognizer = new KaldiRecognizer(model, 16000); recognizer.setWords(true); // 处理音频流数据 audioStream.on('data', (chunk) => { if (recognizer.acceptWaveform(chunk)) { const result = JSON.parse(recognizer.result()); if (result.text.trim()) { transcriptChannel.send(`**${speaker.displayName}**: ${result.text}`); } } }); // 音频流结束时获取最终识别结果 audioStream.on('end', () => { const finalResult = JSON.parse(recognizer.finalResult()); if (finalResult.text.trim()) { transcriptChannel.send(`**${speaker.displayName}**: ${finalResult.text}`); } recognizer.free(); // 释放识别器资源 }); // 处理识别错误 recognizer.on('error', (err) => { console.error(`识别错误(用户:${speaker.displayName}):`, err); recognizer.free(); }); }); }
关键说明
- 使用
voiceConn.receiver接收用户语音流,AudioPlayer仅用于播放音频,不可用于接收 - 为每个说话用户创建独立的
KaldiRecognizer实例,避免识别内容混淆 - 必须设置
selfDeaf: false,否则机器人无法接收频道内的语音 - 音频流结束时调用
recognizer.finalResult()获取完整转录文本,避免遗漏内容 - 识别完成后调用
recognizer.free()释放资源,防止内存泄漏
内容的提问来源于stack exchange,提问作者Dem Snip
相关产品推荐
相关产品推荐

