☰
Airi Discord Bot 集成指南:让 AI 伴侣接入 Discord 语音与文字频道对话
2026/10/17 2:25:56 网站建设 项目流程
  • 人工智能
  • AI Agent
  • MCP 服务
  • 工具调用
  • GUI 自动化
  • 语音
  • 数字人
  • 交互助手

【免费下载链接】airi

💖🧸 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器,目标是接近 Neuro-sama 的高度;支持实时语音聊天、Minecraft 和 Factorio 游玩,支持 Web / macOS / Windows。

项目地址:https://gitcode.com/moeru-ai/airi
点击查看免费下载

本指南围绕 integrations/discord-bot/README.md 展开,讲解如何将 Airi(アイリ)以 Discord Bot 的形式接入你的服务器:用户既可以在文字频道通过 @提及或私信与 Airi 对话,也可以在语音频道直接开口说话,让 Airi 实时聆听、转写并回复。读完本文,你将掌握完整的部署流程(Discord 开发者后台配置、环境变量、启动命令),并理解其背后的双向桥接架构、语音处理管线(Opus 解码 → 转写 → 上下文注入)与可热更新的配置机制,可直接照抄配置跑通一个属于自己的 Discord AI 角色。

一、discord-bot 在 Airi 项目中的定位

integrations/discord-bot是 Airi 官方仓库中的一个独立集成模块(pnpm workspace 中的@proj-airi/discord-bot),其目标正如 README 第一行所述:让 Airi 能在 Discord 语音频道中与你以及众多其他用户对话。它并不自己实现模型推理,而是作为一个"桥接适配器":

  • 面向 Discord 一侧,使用discord.js与@discordjs/voice负责登录、收发消息、加入语音频道、订阅用户音频;
  • 面向 Airi 一侧,使用工作区内部的 server-sdk 客户端(@proj-airi/server-sdk)通过 WebSocket 连接 Airi 的 server-runtime,发送input:text/input:text:voice等输入事件,并订阅output:gen-ai:chat:message等输出事件。

换句话说,Discord 只是 Airi 的"一张嘴"和"一对耳朵",真正的角色人格、记忆与模型对话能力由 Airi 核心服务提供。这也解释了为什么package.json中同时依赖了discord.js、@discordjs/voice和@proj-airi/server-sdk、@proj-airi/server-shared。

二、工作原理总览:Discord ⇄ Airi 的双向事件桥

从 airi-adapter.ts 的构造函数可以看出整个桥的核心是两个客户端对象:

  • discordClient:discord.js的Client,intents 包含Guilds、GuildVoiceStates、GuildMessages、MessageContent、DirectMessages,并启用Partials.Channel;
  • airiClient:ServerChannel(即 server-sdk 的Client),以名称discord注册,声明了它关心的possibleEvents:
    • input:text(文字输入)
    • input:text:voice(语音转写文本输入)
    • input:voice(原始语音输入)
    • module:configure(来自 UI 的配置下发)
    • output:gen-ai:chat:message(Airi 的对话回复)

数据流大致如下:

  1. 用户在 Discord 文字频道 @ 机器人或发私信 → 适配器组装discord元数据并发送input:text;
  2. 用户在语音频道说话 → 音频被解码、缓冲、转写为文字 → 发送input:text:voice与input:text;
  3. Airi 核心生成回复 → 广播output:gen-ai:chat:message→ 适配器按事件中的discord.channelId把文本回复发送回对应频道(超过 2000 字符自动分块)。

这些事件类型在协议层有严格定义,参见 plugin-protocol 的事件类型(WebSocketEventInputText、WebSocketEventInputTextVoice、WebSocketEventInputVoice、OutputGenAiChatMessageEvent),其中discord输入源的元数据结构为Discord(含guildMember、guildId、guildName、channelId)。

注意:input:text:voice的数据结构为{ transcription, ... },input:voice的数据结构为{ audio: ArrayBuffer, ... },而input:text的数据结构为{ text, textRaw, overrides, contextUpdates, ... },三者面向不同粒度的输入场景。

三、快速开始:创建 Discord 应用并开启所需 Intents

3.1 环境准备

该模块是 Airi monorepo 的一部分,需要先获取仓库并安装依赖(依赖管理使用 pnpm workspace):

git clone git@github.com:moeru-ai/airi.git pnpm i

安装完成后进入模块目录即可看到.env模板文件:

cd integrations/discord-bot cp .env .env.local

3.2 在 Discord Developer Portal 创建应用

  1. 打开 Discord Developer Portal(discord.com/developers),点击New Application创建一个新应用——这个应用就是稍后要添加到你服务器的机器人。
  2. 进入Bot标签页,找到Privileged Gateway Intents(特权网关意图)区域,打开以下两项开关:
    • Server Members Intent(服务器成员意图)
    • Message Content Intent(消息内容意图)

这两个 Intents 与 airi-adapter.ts 中discordClient的配置一一对应:GuildMessages+MessageContent用于读取消息内容,GuildVoiceStates用于感知语音频道状态,而 Server Members Intent 用于在summon.ts中通过guild.members.fetch(userId)获取说话成员信息。

  1. 在Privileged Gateway Intents区域上方是Token区域:对于新建的机器人,点击Reset Token生成一个新 Token,复制保存备用。
    • 注意:如果日后忘记或丢失 Token,随时可以再次点击Reset Token重新生成;但生成后需要同步更新到.env.local(或通过下文介绍的 UI 配置方式更新),否则机器人无法登录。

提示:若勾选这些特权 Intent 后机器人仍无法读取消息,请检查应用是否处于审核模式且机器人不在大型服务器上;本地开发/测试建议在小规模私人服务器中进行。

四、环境变量配置详解

创建.env.local后,按 README 要求填写如下凭证。DISCORD_TOKEN与DISCORD_BOT_CLIENT_ID是必填项,其余为 AI 能力相关配置:

DISCORD_TOKEN='' DISCORD_BOT_CLIENT_ID='' OPENAI_MODEL='' OPENAI_API_KEY='' OPENAI_API_BASE_URL='' ELEVENLABS_API_KEY='' ELEVENLABS_API_BASE_URL=''

下表汇总了 README 与仓库内.env模板的全部配置项及其在源码中的实际用途:

配置项用途源码依据
DISCORD_TOKENDiscord 机器人登录令牌,用于client.login()airi-adapter.ts
DISCORD_BOT_CLIENT_ID机器人应用 ID(README 要求配置).env模板;从源码结构看,斜杠命令注册时动态使用登录后的readyClient.user.id
OPENAI_MODEL/OPENAI_API_KEY/OPENAI_API_BASE_URL对话模型相关配置(README 要求配置).env模板
OPENAI_STT_API_BASE_URL/OPENAI_STT_API_KEY/OPENAI_STT_MODEL远程语音转写(STT)服务配置,用于语音频道转写tts.ts 中的openaiTranscribe()
ELEVENLABS_API_KEY/ELEVENLABS_API_BASE_URLTTS 语音合成服务预留配置.env模板
AIRI_TOKENAiri 服务端 WebSocket 鉴权令牌,默认'abcd'index.ts
AIRI_URLAiri server-runtime 的 WebSocket 地址,默认ws://localhost:6121/wsindex.ts

两点补充说明(均以仓库源码为准):

  • AIRI_TOKEN与AIRI_URL不在 README 的配置清单里,但入口文件 index.ts 在构造DiscordAdapter时提供了默认值兜底:airiToken: env.AIRI_TOKEN || 'abcd'、airiUrl: env.AIRI_URL || 'ws://localhost:6121/ws'。默认地址6121端口与 server-runtime 的默认监听配置({ port: 6121, hostname: '127.0.0.1' })一致,即本地跑起 Airi 服务端后无需额外配置即可连通。
  • STT 是独立配置组:OPENAI_MODEL/API_KEY/API_BASE_URL用于对话生成,而语音转写走的是OPENAI_STT_*三个变量。此外 tts.ts 还内置了一个基于@huggingface/transformers的本地 Whisper 管线(Xenova/whisper-medium.en,16kHz、32 位浮点输入),可在无云端 STT 时离线转写;当前语音频道流程(summon.ts)实际调用的是openaiTranscribe()。

五、启动机器人

在仓库根目录(或模块目录)执行:

pnpm run -F @proj-airi/discord-bot start

对应的 npm script 在 package.json 中定义为:

"start": "tsx --env-file=.env --env-file-if-exists=.env.local src/index.ts"

tsx负责直接运行 TypeScript 入口,--env-file=.env与--env-file-if-exists=.env.local依次加载模板与本地覆盖配置(if-exists意味着.env.local不存在也不会报错)。启动流程见 index.ts:

  1. 设置全局日志格式与级别(@guiiai/logg的Format.Pretty/LogLevel.Log);
  2. 用环境变量构造DiscordAdapter;
  3. 调用adapter.start():若DISCORD_TOKEN存在则直接登录 Discord,否则仅启动 WebSocket 客户端并等待 UI 下发配置;
  4. 注册SIGINT/SIGTERM处理,收到信号后调用adapter.stop()优雅退出(销毁 Discord 客户端并关闭 WebSocket)。

启动成功后,Discord 机器人上线时会打印Discord bot ready! User: ...,并自动通过 REST API 注册两个斜杠命令:

  • /ping:回复Pong!,用于连通性测试(见 ping.ts);
  • /summon:把机器人召唤到你当前所在的语音频道(见 summon.ts 的handleJoinChannelCommand)。

六、文字对话:@提及、私信与会话隔离

文字消息处理逻辑位于 airi-adapter.ts 的Events.MessageCreate监听器中,触发条件有两个:

  1. 消息 @ 了机器人(message.mentions.has(this.discordClient.user));
  2. 消息是私信(!message.guild)。

机器人自己的消息会被跳过(if (message.author.bot) return)。@ 提及时会用正则/<@!?\d+>/g把消息中的提及语法剥掉,只把剩余正文发给 Airi;私信则直接使用原文。

发送给 Airi 的input:text事件包含三块关键信息(对应协议类型 WebSocketEventInputText):

  • overrides.messagePrefix:注入"这句话来自谁"的前缀,例如(From Discord user xxx on server 'yyy'):或(From Discord user xxx in Direct Message):,让角色模型知道上下文来源;
  • overrides.sessionId:会话隔离的依据。有guildId时使用discord-guild-{guildId},私信时使用discord-dm-{userId},保证不同服务器、不同私信对各自保持独立记忆;
  • contextUpdates:采用ContextUpdateStrategy.AppendSelf策略,把discord元数据(频道 ID、服务器 ID、成员显示名/昵称)追加进上下文,供后续输出路由使用。

回复链路同样在 airi-adapter.ts 中:监听output:gen-ai:chat:message,从事件数据中取出message.content和gen-ai:chat.input.data.discord.channelId,然后channels.fetch对应频道并发送文本。若回复超过 Discord 单条消息的2000 字符上限,会按"换行 → 空格 → 硬切"的优先级自动分块发送。

七、语音对话:从 Opus 音频到文本回复的完整管线

语音能力是integrations/discord-bot最有技术含量、也是 README 定位("在语音频道与你和其他用户对话")的核心所在,实现在 summon.ts 的VoiceManager中。整条管线分为四个阶段:

7.1 加入语音频道

/summon命令触发handleJoinChannelCommand:校验调用者当前是否在语音频道(不在则回复Please join a voice channel first.),随后调用joinChannel():

  • 若该服务器已有旧连接,先destroy()并清理所有 stream 与 monitor;
  • 通过joinVoiceChannel()(来自@discordjs/voice)以selfDeaf: false、selfMute: false加入,确保机器人能正常"听到"并"说话";
  • 用Promise.race([entersState(Ready), entersState(Signalling)])在20 秒超时内确认连接建立,成功后回复Joined: {channel}.;
  • 为连接注册stateChange/error监听,并在receiver.speaking上监听用户的start/end说话事件;
  • 若机器人有DeafenMembers权限,会尝试setDeaf(false)与setMute(false)确保自身不静音。

断线处理也内置了自动重连:状态变为Disconnected时先尝试 5 秒内恢复到Signalling或Connecting,若超时则判定为真断开,销毁连接并清理资源。

7.2 音频接收与 Opus 解码

当用户开始说话,monitorMember()通过connection.receiver.subscribe(userId)订阅该用户的音频流,随后:

  • 用 opus.ts 中的OpusDecoder(基于opusscript,采样率DECODE_SAMPLE_RATE = 16000、单声道,见 audio.ts 常量)把 Opus 数据解码为 PCM;
  • 通过pipeline()把接收流接到解码器上,形成"Discord 接收流 → OpusDecoder → PCM"的 Node 流式管线;
  • 解码出的 PCM 数据由 audio-monitor.ts 的AudioMonitor缓冲(默认上限 10,000,000 字节,超出则从头部丢弃),并在speakingStopped或流结束时把缓冲数据作为完整片段交给下一步。

7.3 缓冲、防抖与转写

缓冲数据进入handleUserStream(),每收到一段就刷新"最后活跃时间"并触发debouncedProcessTranscription():

  • 防抖窗口 1500ms:等待用户说完(1.5 秒静默)再开始转写,避免把半句话提前送进 STT;
  • 若机器人正在播放回复(activeAudioPlayer存在)或正在处理上一次转写(processingVoice),则丢弃新缓冲,防止自问自答;
  • 防抖到期后调用processTranscription():
    1. Buffer.concat合并本次缓冲;
    2. 通过 audio.ts 的convertOpusToWav()生成 44 字节 WAV 头(16bit PCM 单声道)并拼接成完整 WAV;
    3. 调用 tts.ts 的openaiTranscribe()(Blob封装 +@xsai/generate-transcription),使用OPENAI_STT_MODEL完成语音转写;
    4. 校验转写结果非空且不含[BLANK_AUDIO]占位(isValidTranscription);
    5. 同时发送input:text:voice(带transcription字段)与input:text(带text字段)两个事件给 Airi,均携带discord上下文。

7.4 回复播放与打断机制

VoiceManager同时提供了playAudioStream()用于播放 TTS 音频(createAudioPlayer+createAudioResource,NoSubscriberBehavior.Pause),并且在 PCM 数据监听中内置了语音打断逻辑:当机器人正在播放时,计算用户音频的滑动窗口音量(VOLUME_WINDOW_SIZE = 30帧,SPEAKING_THRESHOLD = 0.05),一旦用户音量均值超过阈值就cleanupAudioPlayer()停止当前播放,把说话权交还给用户。该设计借鉴了 elizaOS 的 client-discord voice 实现(源码注释中明确标注了出处)。

需要说明的是:package.json已声明@xsai/generate-speech依赖、.env也预留了ELEVENLABS_API_KEY/ELEVENLABS_API_BASE_URL,且playAudioStream已就绪;但从当前源码结构看,output:gen-ai:chat:message处理器仅把文本回复发回文字频道,尚未发现把回复文本送入 TTS 生成并调用playAudioStream的接线代码——即语音"听"(STT)链路完整,而"说"(TTS)链路已具备全部构件、有待进一步接入。

八、通过 UI 热更新配置:module:configure

除了.env.local,机器人还支持运行时从 Airi 的 UI 下发配置。适配器订阅了module:configure事件(见 airi-adapter.ts),配置对象形如{ token?: string, enabled?: boolean },并经过isDiscordConfig类型守卫校验。其行为矩阵如下:

配置行为
enabled === false若机器人已就绪则client.destroy()下线
enabled === true但无token警告并停止机器人(如果正在运行)
token变化或客户端未就绪销毁旧连接,用新 Token 重新login()
配置非法打日志跳过,不影响现有连接

整个处理用isReconnecting标志做了重入保护:若上一次配置尚未处理完,新的module:configure事件会被跳过并记录警告。这也呼应了 README 的提示——Token 丢失时既可以直接改.env.local,也可以"通过 UI 配置"完成更新。

九、事件协议一览

以下事件是discord-bot与 Airi 核心之间通信的全部协议面(声明于DiscordAdapter的possibleEvents,类型定义见 plugin-protocol/src/types/events.ts):

事件名方向数据要点触发场景
input:textBot → Airitext、overrides、contextUpdates、discord文字频道 @ 提及 / 私信;语音转写结果二次送入
input:text:voiceBot → Airitranscription、discord语音频道说话并完成转写
input:voiceBot → Airiaudio: ArrayBuffer原始语音输入(已声明,当前流程未实际发送)
module:configureAiri → Botconfig: { token, enabled }UI 下发 Discord 配置,支持热更新/重连
output:gen-ai:chat:messageAiri → Botmessage.content、gen-ai:chat.input.data.discordAiri 回复生成,路由回对应 Discord 频道

十、参考与致谢

本模块的实现站在了多个开源项目的肩膀上(详见 README 致谢段):

  • 音频接收与处理实现参考了 CharacterAIVoice 项目的VoiceHandler.js;
  • 机器人骨架与语音示例参考了 discord.js 官方的voice-examples;
  • 底层使用discord.js与@discordjs/voice这两个优秀库;
  • 语音监听与打断的部分思路在源码注释中标注了与 elizaOSclient-discord/src/voice.ts的渊源。

如果你想快速验证整条链路,建议按以下顺序自测:先确认 Airi server-runtime 已在本机6121端口运行 → 启动discord-bot→ 在服务器中/ping验证连通 → @ 机器人发送一句文字验证文字对话 → 进入语音频道/summon验证语音聆听与转写。全部通过后,一个"住在 Discord 里的 Airi"就正式上线了。

  • 人工智能
  • AI Agent
  • MCP 服务
  • 工具调用
  • GUI 自动化
  • 语音
  • 数字人
  • 交互助手

【免费下载链接】airi

💖🧸 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器,目标是接近 Neuro-sama 的高度;支持实时语音聊天、Minecraft 和 Factorio 游玩,支持 Web / macOS / Windows。

项目地址:https://gitcode.com/moeru-ai/airi
点击查看免费下载

相关推荐

上一篇:一个ddraw.dll让老游戏重获新生:DDrawCompat上手全指南
下一篇:Humanizer 本地化序数词引擎:IOrdinalizer 接口的契约、实现与扩展实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询