logo
/
Discord ChatGPT 机器人
Get a copy
43
快速开发一个 Discord 机器人,并且使用命令行与 ChatGPT 交互。
  • README.md
    arrow

    开发一个 Discord ChatGPT 机器人

    我们在构建什么

    在本教程中,我们将使用 JavaScript 与 AirCode 引导你构建一个 Discord 应用程序,并且使用命令行方式让它与 ChatGPT 交互。

    最终效果图:

    在本教程使用到的资源

    第一步: 创建应用和机器人

    首先,如果你还没有 Discord 应用,请在开发者后台创建一个应用:

    1. 前往 Discord Developer Portal (使用你的 Discord 帐户登录)。点击右上角的 "新建应用" 按钮。给应用取一个名称,然后点击 "创建"。

    1. 复制你的 Public Key 公钥和 Application ID 应用 ID,并将它们保存在本地某个地方(我们稍后会用到)

    1. 在屏幕左侧的 "Bot" 选项卡上点击 "Bot" 按钮,然后点击右侧的 "添加 Bot" 按钮。创建完机器人后,重置你的机器人 Token,并将其安全地保存在某个地方(我们稍后会用到)

    创建应用和机器人后,你可以添加图标、描述等进行自定义。

    第二步: 设置范围和权限

    点击左侧边栏中的 OAuth2,然后选择 URL Generator。

    添加两个 Scope 范围:

    • applications.commands:允许你的应用创建命令行。
    • bot:添加你的机器人用户,在选择 bot 后,你还可以为你的机器人选择不同的权限。目前只需选中 "Send Messages" 进行发送消息。

    复制上面生成的 URL,并将其粘贴到浏览器中。你将会被引导完成授权流程,如下图选择一个你的 Discord 服务器进行授权添加 Bot 以及安装命令行。

    授权完成后,回到你的 Discord 服务器中,能看到频道中消息提示机器人 GPT 已经加入了服务器。✨

    第三步: Get a copy 并且部署应用

    本指南使用 AirCode 来托管代码。在获取副本之前,请确保你拥有一个 AirCode 帐户。

    1. 前往 Discord ChatGPT 应用. 点击 "Get a copy" 按钮。

    1. 你将会重定向到创建应用页面,使用默认名称或输入自己的应用名称,然后点击“创建”。

    将 Bot Token、OpenAI Key、Public Key、Application ID、先前从平台保存的内容粘贴到环境变量中。确保它们在正确的 key value 键值位置。

    如果没有 OpenAI API Key,需要准备帐号登陆 OpenAI 的控制台,点「Create new secret key」生成并且复制你的 OpenAI Key。

    点击“Deploy”按钮来部署所有文件,在部署后环境变量的更改将生效。

    第四步: 注册命令行

    该项目包含一个注册命令的脚本,你可以使用它来安装命令,该脚本定义在 register-command.js 中。

    选择 register-command.js 文件,将会看到如下的一个调用网址,复制该网址并粘贴到浏览器的 URL 地址栏中进行访问。

    你将在你的 Discord 服务器中注册 /chatgpt 命令行。

    第五步: 处理交互

    为了使你的应用能够接收命令行请求,Discord 需要一个公共 URL 来发送它们。

    选择 interactions.js 文件,你将找到如下的一个调用 URL,复制该链接。

    返回 Discord 应用程序,在应用的“常规信息”页面上,有一个“交互式端点 URL”选项,将从 AirCode 复制的调用 URL 粘贴到里面。

    点击“保存更改”按钮,确保保存成功。

    通过输入命令 "/chatgpt" 来测试你的 ChatGPT 机器人。

    反馈

  • interactions.js
    arrow
    1// @see https://docs.aircode.io/guide/functions/
    2const axios = require('axios');
    3const nacl = require('tweetnacl');
    4const { Configuration, OpenAIApi } = require('openai');
    5
    6const {
    7  EmbedBuilder,
    8} = require('discord.js');
    9
    10const {
    11  verifyKey,
    12  InteractionType,
    13  InteractionResponseType,
    14} = require('discord-interactions');
    15
    16const apiKey = process.env.OPENAI_KEY;
    17
    18const discordToken = process.env.DISCORD_BOT_TOKEN;
    19
    20// Your public key can be found on your application in the Developer Portal
    21const discordPublicKey = process.env.DISCORD_PUBLIC_KEY;
    22
    23// Slash command's name you use tointeract with bots on Discord after typing /
    24const slashCommandName = 'chatgpt';
    25
    26// Use openAI API to fetch ChatGPT completion
    27const getOpenAIChatCompletion = async (question) => {
    28  
    29  try {
    30    const configuration = new Configuration({
    31      apiKey,
    32    });
    33
    34    const openai = new OpenAIApi(configuration);
    35
    36    const completion = await openai.createChatCompletion({
    37      // OpenAI models https://platform.openai.com/docs/models
    38      model: 'gpt-3.5-turbo',
    39      messages: [{ role: 'assistant', content: question }],
    40    });
    41
    42    console.log(
    43      'ChatGPT completion data:',
    44      completion.data.choices[0].message.content.trim()
    45    );
    46    return {
    47      code: 0,
    48      data: completion.data.choices[0].message.content.trim(),
    49    };
    50  } catch (error) {
    51    console.error(`OpenAI api error: ${error.message}`);
    52    return {
    53      code: 1,
    54      message: `OpenAI api error: ${error.message}`,
    55    };
    56  }
    57};
    58
    59// Build an Embed (rich embed) object for replied message
    60const createResponseEmbed = (response) => {
    61  const embed = new EmbedBuilder()
    62    .setColor('#37393E')
    63    .setDescription(response);
    64
    65  return { embeds: [embed] };
    66};
    67
    68module.exports = async function (params, context) {
    69
    70  console.log('params context', params);
    71
    72  // Get signature and timestamp from headers
    73  const signature = context.headers['X-Signature-Ed25519'];
    74  const timestamp = context.headers['X-Signature-Timestamp'];
    75  // Get rawBody content
    76  const body = context.rawBody;
    77
    78  // Verify whether the request from discord
    79  const isVerified = nacl.sign.detached.verify(
    80    Buffer.from(timestamp + body),
    81    Buffer.from(signature, 'hex'),
    82    Buffer.from(discordPublicKey, 'hex')
    83  );
    84
    85  if (!isVerified) {
    86    context.status(401);
    87    return 'Invalid request signature';
    88  }
    89
    90  // Interaction type and data
    91  const { type, id, data, token } = params;
    92
    93  console.log('params', params);
    94  // Handle verification requests reply with PONG
    95  if (type === InteractionType.PING) {
    96    return { type: InteractionResponseType.PONG };
    97  };
    98
    99  // Handle the user registered interaction command
    100  if (
    101    type === InteractionType.APPLICATION_COMMAND &&
    102    data.name === slashCommandName
    103  ) {
    104    const userInput = data.options[0].value;
    105
    106    // Immediately acknowledge the interaction
    107    try {
    108      await axios.post(
    109        `https://discord.com/api/v9/interactions/${id}/${token}/callback`,
    110        {
    111          type: InteractionResponseType.DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE,
    112        }
    113      );
    114    } catch (error) {
    115      console.error(
    116        'Error acknowledging the interaction:',
    117        error
    118      );
    119      return;
    120    };
    121
    122    const response = await getOpenAIChatCompletion(userInput);
    123    console.log('response form OpenAI ChatCompletion: ', response);
    124
    125    // Patch message
    126    if (response.code === 0) {
    127      try {
    128        await axios.patch(
    129          `https://discord.com/api/v9/webhooks/${process.env.DISCORD_APP_ID}/${token}/messages/@original`,
    130          {
    131            content: `> ${userInput} \n ${response.data}`,
    132          }
    133        );
    134      } catch (error) {
    135        console.error('Error editing the original response:', error.message);
    136        return;
    137      }
    138    };
    139  }
    140};
    141
  • register-command.js
    arrow
    1// @see https://docs.aircode.io/guide/functions/
    2const axios = require('axios');
    3const Discord = require('discord.js');
    4
    5async function registerCommands(appId, commands) {
    6  const { DISCORD_BOT_TOKEN } = process.env;
    7
    8  try {
    9    // Use discord sdk register chatgpt command
    10    const rest = new Discord.REST({ version: '10' }).setToken(
    11      DISCORD_BOT_TOKEN
    12    );
    13    const data = await rest.put(Discord.Routes.applicationCommands(appId), {
    14      body: commands,
    15    });
    16
    17    console.log('Command register response:', data);
    18
    19    if (data) {
    20      return {
    21        success: true,
    22        message: `WOW, slash command ${data
    23          .map((command) => `/${command.name}`)
    24          .join(' ')} registered`,
    25      };
    26    }
    27  } catch (err) {
    28    console.error('Error from registering commands:', err);
    29  }
    30}
    31
    32module.exports = async function (params, context) {
    33  console.log('Received params:', params);
    34  const slashCommandName = 'chatgpt';
    35
    36  const ChatGPTCommand = {
    37    name: slashCommandName,
    38    description: 'Interact with ChatGPT',
    39    options: [
    40      {
    41        name: 'prompt',
    42        type: 3,
    43        description: 'The message sending to ChatGPT',
    44        required: true,
    45      },
    46    ],
    47  };
    48
    49  const commands = [ChatGPTCommand];
    50  // Register ChatGPT command
    51  const res = await registerCommands(process.env.DISCORD_APP_ID, commands);
    52
    53  return res;
    54};
    55
  • Runtime
    arrow
    • Node.js versionnode/v16
    • Function execution timeout90 s
  • Dependencies
    arrow
    • aircode0.4.1
    • axios1.4.0
    • discord-interactions3.4.0
    • discord.js14.11.0
    • openai3.2.1
    • tweetnacl1.0.3
  • Environments
    arrow
    arrow
    • DISCORD_BOT_TOKEN
    • OPENAI_KEY
    • DISCORD_PUBLIC_KEY
    • DISCORD_APP_ID