Skip to main content

Overview

The Discord integration allows AIRI to join voice channels, listen to users, transcribe speech, and respond with both text and voice. It supports slash commands, mentions, and direct messages.

Features

  • Voice Channel Support: Join voice channels and participate in conversations
  • Speech-to-Text: Automatically transcribe user voice input
  • Text-to-Speech: Respond with natural-sounding voice
  • Slash Commands: Interactive commands like /summon and /ping
  • Text Chat: Respond to mentions and DMs
  • Multi-Guild Support: Work across multiple Discord servers simultaneously

Prerequisites

  • Node.js 18 or higher
  • A Discord bot token
  • OpenAI API key (for LLM and transcription)
  • ElevenLabs API key (for TTS)
  • AIRI server runtime running

Setup

1. Create Discord Bot

  1. Go to Discord Developer Portal
  2. Click New Application
  3. Go to Bot section and click Add Bot
  4. Enable these Privileged Gateway Intents:
    • Server Members Intent
    • Message Content Intent
  5. Copy your Bot Token
  6. Copy your Application ID (from General Information)

2. Invite Bot to Server

Use this URL, replacing YOUR_CLIENT_ID with your Application ID:
https://discord.com/api/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=36703296&scope=bot%20applications.commands
Required Permissions:
  • Read Messages/View Channels
  • Send Messages
  • Connect to Voice
  • Speak in Voice
  • Use Voice Activity

3. Configure Environment

Navigate to the Discord bot service:
cd services/discord-bot
cp .env .env.local
Edit .env.local:
# Discord Configuration
DISCORD_TOKEN='your-bot-token-here'
DISCORD_BOT_CLIENT_ID='your-application-id-here'

# AIRI Server Connection
AIRI_URL='ws://localhost:6121/ws'
AIRI_TOKEN='abcd'

# OpenAI Configuration
OPENAI_API_KEY='sk-...'
OPENAI_MODEL='gpt-4o'
OPENAI_API_BASE_URL='https://api.openai.com/v1'

# ElevenLabs Configuration (for TTS)
ELEVENLABS_API_KEY='your-elevenlabs-key'
ELEVENLABS_API_BASE_URL='https://api.elevenlabs.io/v1'

4. Install Dependencies

From the project root:
pnpm install

5. Start the Bot

pnpm run -F @proj-airi/discord-bot start
You should see:
Discord bot ready! User: YourBotName#1234

Usage

Voice Commands

  1. Join a voice channel
  2. Use the /summon command in any text channel
  3. AIRI will join your voice channel
  4. Start speaking - AIRI will transcribe and respond
AIRI uses Voice Activity Detection (VAD) to automatically detect when users are speaking.

Text Commands

Slash Commands:
  • /ping - Check if the bot is responsive
  • /summon - Invite AIRI to your current voice channel
Mentions: Mention the bot in any channel:
@AIRI what's the weather like today?
Direct Messages: Send a DM to the bot - no mention required.

Code Examples

Basic Adapter Setup

From services/discord-bot/src/adapters/airi-adapter.ts:55:
import { Client as AiriClient } from '@proj-airi/server-sdk'
import { Client, GatewayIntentBits } from 'discord.js'

export class DiscordAdapter {
  private airiClient: AiriClient
  private discordClient: Client

  constructor(config: DiscordAdapterConfig) {
    // Initialize Discord client
    this.discordClient = new Client({
      intents: [
        GatewayIntentBits.Guilds,
        GatewayIntentBits.GuildVoiceStates,
        GatewayIntentBits.GuildMessages,
        GatewayIntentBits.MessageContent,
        GatewayIntentBits.DirectMessages,
      ],
    })

    // Initialize AIRI client
    this.airiClient = new AiriClient({
      name: 'discord',
      possibleEvents: [
        'input:text',
        'input:text:voice',
        'input:voice',
        'module:configure',
        'output:gen-ai:chat:message',
      ],
      token: config.airiToken,
      url: config.airiUrl,
    })
  }
}

Sending User Input to AIRI

From services/discord-bot/src/adapters/airi-adapter.ts:261:
// Handle text messages
this.discordClient.on(Events.MessageCreate, async (message) => {
  if (message.author.bot) return
  
  const isMentioned = message.mentions.has(this.discordClient.user)
  const isDM = !message.guild
  
  if (isMentioned || isDM) {
    const content = message.content
      .replace(/<@!?\d+>/g, '')
      .trim()
    
    // Send to AIRI
    this.airiClient.send({
      type: 'input:text',
      data: {
        text: content,
        discord: {
          channelId: message.channelId,
          guildId: message.guildId,
          guildMember: {
            id: message.author.id,
            displayName: message.member?.displayName,
          },
        },
      },
    })
  }
})

Receiving Responses from AIRI

From services/discord-bot/src/adapters/airi-adapter.ts:159:
// Handle output from AIRI system
this.airiClient.onEvent('output:gen-ai:chat:message', async (event) => {
  const message = event.data.message
  const discordContext = event.data['gen-ai:chat'].input.data.discord
  
  if (message?.content && discordContext?.channelId) {
    const channel = await this.discordClient.channels.fetch(
      discordContext.channelId
    )
    
    if (channel?.isTextBased()) {
      // Split long messages if needed
      if (message.content.length <= 2000) {
        await channel.send(message.content)
      } else {
        // Handle chunking for messages over 2000 chars
        // ...
      }
    }
  }
})

Voice Channel Integration

From services/discord-bot/src/bots/discord/commands/summon.ts:157:
import { joinVoiceChannel, createAudioPlayer } from '@discordjs/voice'

async joinChannel(interaction, channel) {
  const connection = joinVoiceChannel({
    channelId: channel.id,
    guildId: channel.guild.id,
    adapterCreator: channel.guild.voiceAdapterCreator,
    selfDeaf: false,
    selfMute: false,
  })

  // Wait for connection to be ready
  await Promise.race([
    entersState(connection, VoiceConnectionStatus.Ready, 20_000),
    entersState(connection, VoiceConnectionStatus.Signalling, 20_000),
  ])

  // Listen to voice data
  connection.receiver.speaking.on('start', (userId) => {
    this.monitorMember(userId, channel.id)
  })
}

Configuration

Dynamic Reconfiguration

The Discord bot supports runtime configuration updates via the module:configure event:
this.airiClient.onEvent('module:configure', async (event) => {
  const { token, enabled } = event.data.config
  
  if (enabled === false) {
    await this.discordClient.destroy()
    return
  }
  
  if (token && this.discordToken !== token) {
    this.discordToken = token
    await this.discordClient.login(this.discordToken)
  }
})

Voice Settings

  • Sample Rate: 48000 Hz (Opus)
  • Channels: Stereo
  • Transcription: OpenAI Whisper
  • TTS: ElevenLabs

Troubleshooting

Bot is not responding

  1. Check the bot is online in Discord
  2. Verify the bot has permissions in the channel
  3. Check logs for connection errors

Voice not working

  1. Ensure you have ELEVENLABS_API_KEY configured
  2. Verify the bot has “Connect” and “Speak” permissions
  3. Check that voice gateway is connected in logs

Commands not showing

  1. Commands are registered when the bot starts
  2. Wait a few minutes for Discord to sync
  3. Try reinviting the bot with the correct scope

Next Steps

Configure Voice Settings

Customize voice and transcription settings

Telegram Integration

Add AIRI to Telegram

Build docs developers (and LLMs) love