Slash Command: /join
Relevant source files
The following files were used as context for generating this wiki page:
The /join command is the entry point for the Discord Live Translator bot's core functionality. Its primary purpose is to make the bot connect to the voice channel of the user who invoked the command. Upon successfully joining, it plays a connection sound and, crucially, sets up the necessary event listeners to begin the real-time voice translation process for users who have opted in via the /start command.
This command is a prerequisite for all voice-related operations. After the bot has joined a channel, users can enable translation for themselves with /start <language>. The bot will remain in the channel until explicitly removed with the /leave command.
Command Definition
The /join command is defined using the @discordjs/builders package. It is a simple command with no options.
// src/commands/join.ts:22-24
export const joinCommand = new SlashCommandBuilder()
.setName('join')
.setDescription('Move the bot into your voice channel');
This definition is then exported and included in the list of commands to be deployed to Discord's API, either for a specific guild or globally.
Sources: src/commands/join.ts:22-24, src/interactions.ts:10
Command Execution Flow
When a user executes the /join command, the joinCommandHandler function is triggered. The handler performs a series of validations before attempting to connect to the voice channel.
Sources: src/app.ts:29-32, src/commands/join.ts:26-125
Initial Validations
Before proceeding, the command handler performs several checks to ensure the command can be executed successfully. If any check fails, it sends an ephemeral error message to the user and terminates execution.
| Check | Description | Failure Message |
|---|---|---|
| User Context | Verifies that the interaction comes from a guild member. | An error occurred! :grimacing: |
| User in Voice Channel | Checks if the invoking member is currently in a voice channel. | You are currently not in a voice channel! :x: |
| Bot Permissions | Ensures the bot has Connect and Speak permissions in the user's voice channel. | The bot is missing permission for your channel to connect and/or speak! :grimacing: |
Sources: src/commands/join.ts:27-50
The following diagram illustrates the initial validation and connection logic.
This diagram shows the step-by-step logic flow of the /join command handler. Sources: src/commands/join.ts:27-125
Voice Connection
If all validations pass, the bot proceeds to establish a voice connection.
- Get or Create Connection: It first checks if a voice connection already exists for the guild using
getVoiceConnection.- If no connection exists (
isNewConnection = true), it creates a new one usingjoinVoiceChannel, connecting to the user's current channel. - If a connection already exists, it uses
connection.rejoin()to move to the user's channel if different.
- If no connection exists (
- Wait for Ready State: It waits for the connection to enter the
VoiceConnectionStatus.Readystate with a 20-second timeout. - Initialize Audio Player: It initializes an
AudioPlayerfor the guild via theaudioQueueservice and subscribes the voice connection to it. - Play Connection Sound: It plays a pre-recorded sound file (
audio/connect.mp3) to signal its arrival.
Sources: src/commands/join.ts:60-80, src/utils/AudioQueue.ts:10-14
Real-Time Translation Listener
The most critical function of the /join command is to set up the listener that powers the live translation. This is only done when a new voice connection is established.
After the connection sound finishes playing (indicated by the AudioPlayerStatus.Idle event), a listener is attached to the connection.receiver.speaking event. This listener triggers whenever any user in the channel starts speaking.
Sources: src/commands/join.ts:82-117
Translation Pipeline
The speaking event listener initiates a multi-step pipeline to process the user's speech:
- Record Audio: The
recordAudiofunction is called, which subscribes to the user's voice stream. It captures the Opus audio packets, decodes them into raw PCM data, and saves them as a.wavfile. The recording stops after a short period of silence (100ms). - Recognize Recording (Speech-to-Text): The resulting
.wavfile is sent to a speech-to-text (STT) service (env.STT_HOST). This service, a Rust-based Actix web server using the Whisper model, transcribes the audio into text. - Translate Text: If the user's target language (retrieved from
settingsStorage) is not English, the transcribed text is sent to a translation service (env.TRANSLATION_HOST) to be translated into the target language. - Read Text (Text-to-Speech): The final text (either the original transcription or the translation) is passed to the
readTextfunction. This function uses a Python script to generate a new.wavfile from the text using a Text-to-Speech (TTS) model. - Play Translation: The generated audio file is added to the
audioQueueand played back into the voice channel for all users to hear.
The following sequence diagram visualizes the entire translation pipeline initiated after /join sets up the listener.
This diagram shows the sequence of events from a user speaking to the translated audio being played back in the channel. Sources: src/commands/join.ts:90-116, src/processors/recordAudio.ts:28-46, src/processors/recognizeRecording.ts:6-25, src/processors/readText.ts:9-23, stt/src/main.rs
Summary
The /join command is the foundational step for using the Discord Live Translator. It manages the bot's presence in a voice channel and, more importantly, activates the sophisticated, event-driven pipeline that listens for speech, transcribes it, translates it, and synthesizes it back into audio for real-time, multilingual communication. Its successful execution is the gateway to the bot's entire feature set.