很久没有在掘金写文章啦,当然也是好久没有写文章啦,这篇文章会介绍如何使用 Dify 去做一个 Discord 群聊机器人
准备
你需要以下一些东西,
GitHub – crazywoola/hareporter: A simple discord bot
环境变量
在 .env
中配置前三个环境变量,cp .env.example .env
ini复制代码DISCORD_TOKEN=YOUR_BOT_TOKEN
DISCORD_ID=YOUR_BOT_ID
DIFY_API_KEY=YOUR_DIFY_API_KEY
POSTGRES_USER=postgres
POSTGRES_PASSWORD=botbot
POSTGRES_DB=bot
POSTGRES_PORT=5432
POSTGRES_HOST=db
项目目录结构
csharp复制代码.
├── Dockerfile
├── README.md
├── docker-compose.yml
├── package.json
├── src
│ ├── commands
│ │ ├── ask.js
│ │ └── ping.js
│ ├── db.js
│ └── index.js
└── yarn.lock
在 src/commands
目录下面是是 Discord Slash Command 的配置,db.js
是一个简单的数据库,index.js
则是这个程序主要的逻辑。
原理解释
我们主要用到了两个
Discord
我们用到了三个主要的函数
arduino复制代码discord.login process.env.DISCORD_TOKEN discord.once Events.ClientReady discord.on Events.InteractionCreate
Discord 的机器人也是有身份的 🆔,不过它是通过 Token 这种方式登录的,登录之后,会触发一个一次性的事件也就是 ClientReady
我们在这里会做一些初始化的操作例如
- 获取应用的信息
- 同步我们塑料的数据库
- 注册我们写的 Command
Slash Command
通常一个简单的 PingPong Command 非常之简单,以下代码定义了一个叫做 ping
的命令,描述为 Replies with Pong!
css复制代码import { SlashCommandBuilder } from '@discordjs/builders';
export default {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with Pong!')
}
我们需要在函数 discord.on Events.InteractionCreate
中处理这个 Command,⚠️这里需要注意,需要在3s之内回复,不然就会有红色的提示告诉你机器人无响应。所以我们需要使用到以下方法
css复制代码await message.deferReply({ ephemeral: false });
await message.followUp({ content: '...' });
// or
message.reply({ content: '...' });
第一种方式会展示一个类似于输入中的状态,而不用直接回复到用户,所以它需要特殊的方法来回复也就是 followUp
或者第二种方法是直接回复一个内容,一般如果是 ping pong 这种简单命令就可以用第二种方法回复。
我们这里使用到 Dify 的对话 API 则需要考虑流式这种方式,所以我们不能够一次性的返回出来,这样显得机器人没有什么感情 🤖️️️,所以我们还需要 message.editReply({ content: msg || '😄' });
用来修改已经发送的内容。
Dify
在这个部分,我们需要特别注意的就是拼接请求参数和处理流式的返回,也就是在开始我们初始化的过程中获取的应用信息,因为有一些应用在 prompt 中自定义了参数,而我们需要把这个参数传过去,不然就会是 400 的错误❌
ini复制代码 const response = await chatClient.createChatMessage(
inputs,
query,
user.external_id,
true,
conversation.conversation_id
);
const stream = response.data;
这个函数接受了上述的参数,其中 inputs user.external_id conversation.conversation_id
这三个需要特别注意⚠️不要写错了,如果第一次对话的话 conversation.conversation_id
应该是 null
,user.external_id
只需要在应用内保证唯一,规则是自定义的。inputs
则需要和之前获取到的应用信息一一对应。
那么接下来就是处理 stream
了,在 Node 中处理其实是非常简单的,我们这里简单的处理了 data end
两种情况。在 data chunk
中使用 toString()
方法可以获取到服务器返回的数据,大概是一个 data: { ... }
这种形式,所以我们需要获取后面 json 里面的内容也就是需要 parse,但是有时候可能会出错,所以我们这里就简单的 catch 住就行。
ini复制代码const completeString = chunk.toString();
parsed = JSON.parse(completeString.slice(completeString.indexOf('{')));
end
中我们可以处理一下完整的消息,再给发一遍就行。😂