简介

我最近参与了一个附带项目,该项目涉及为 Twitch.tv 上的机器人构建自定义聊天命令。这些命令本身需要使用 Google Sheets API 进行大量繁重的工作——这是 Node.js 服务器的完美候选者。

这不是我第一次在 Twitch 或 Discord 上完成自定义机器人工作。对于以前的项目,我总是启动一个自定义服务器来管理然后部署到 Heroku(有成本)的机器人。然而,在对这个新项目进行了几个小时的初步工作后,我发现使用现代无服务器技术处理机器人命令会容易得多。毕竟,每个机器人命令只是一个函数。

理论上,这可以使用任何可以让您在没有服务器的情况下轻松托管 API 端点的方法来完成。我选择 Next.js 是因为很多类似的与机器人无关的功能工作都存在于同一个 Next.js 项目中。


工作原理

Nightbot UrlFetch 命令调用 Next.js API 路由,该路由随后返回机器人的聊天响应

  • 🤖 您的 Twitch 频道正在运行Nightbot,它支持个基于 API 的自定义“UrlFetch”命令。 Nightbot 可免费使用,只需不到 20 秒即可在您的频道上进行设置。

  • 👨u200d💻 您使用Next.js 的 API 路由支持构建 serverless 后端微服务功能。

  • 😎 您免费将项目部署到Vercel或Netlify。

  • 📡 您使用 Nightbot利用 UrlFetch 和新部署的 API 路由创建自定义命令。


🔧 让我们构建它

设置一个新的 Next.js 项目

让我们创建一个新的 Next.js 项目。我将在这个项目中使用 TypeScript,但这可以很容易地适应 JavaScript。

在您要创建项目的目录中的终端中,运行:

npx create-next-app --example with-typescript

OR

yarn create next-app --example with-typescript

进入全屏模式 退出全屏模式

几分钟后,您的项目应该准备就绪,可以使用npm run devyarn dev启动开发服务器。

添加新的API路由

在 Next.js 中创建无服务器函数非常简单,感觉就像在作弊。您的项目中应该有一个 pages 文件夹。在 pages 中创建一个 api 文件夹,并在其中创建一个新文件:ping.ts。你的文件结构应该是这样的(我没有修改 TypeScript 示例项目):

页 > api > ping.ts

随着您的开发服务器运行在yarn dev,http://localhost:3000/api/ping现在自动映射到您的 ping.ts 文件!但它还没有做任何事情。

使 API 路由对 Nightbot 有用

我们的自定义聊天命令将非常简单。不会涉及繁重的工作。对于本文,我们希望命令打个招呼,打印发起者的用户名,并打印当前频道。像这样:

正在运行的聊天命令

让我们开始编码。打开 ping.ts 并将此内容粘贴到:

// ping.ts

import { NextApiRequest, NextApiResponse } from 'next';

export default async function (req: NextApiRequest, res: NextApiResponse) {
  res.status(200).send('Hello!');
}

进入全屏模式 退出全屏模式

在本地开发服务器运行(npm run devyarn dev)的情况下,如果您访问 localhost:3000/api/ping,您应该会看到“Hello!”打印到屏幕上。凉爽的!

如果这是您的第一个 Next.js 牛仔竞技表演,请注意以下几点:

  • reqres可能看起来像传统的 Express.js 请求和响应参数,但_它们不是_。NextApiRequestNextApiResponse类似于 Express。 Docshereon response helpers 可能有用。

  • 如果这_all_看起来像月亮语言,Next.js API 路由文档是一个很好的开始。

  • 默认情况下,Nightbot 需要纯文本响应。JSON 受支持,但超出了本文的范围。

好的,我们在屏幕上打印“Hello”,但是用户名和当前频道呢?当 Nightbot 发送 API 请求时,它也会发送带有所有元数据的标头!有关这些标题的信息可以在 UrlFetch 文档页面](https://docs.nightbot.tv/commands/variables/urlfetch)上找到[:

UrlFetch 标头文档

我们对Nightbot-UserNightbot-Channel特别感兴趣。 Nightbot 将这些标头中的数据作为查询字符串发送,如下所示:

req.headers['nightbot-channel'] = 
 'name=kongleague&displayName=KongLeague&provider=twitch&providerId=454709668';

req.headers['nightbot-user'] =
  'name=wescopeland&displayName=WesCopeland&provider=twitch&providerId=52223868&userLevel=moderator'

进入全屏模式 退出全屏模式

我们可以使用 JavaScript 内置的URLSearchParams构造函数来非常容易地解析这些。将这些函数添加到您的 ping.ts 文件中:

// somewhere in ping.ts

const parseNightbotChannel = (channelParams: string) => {
  const params = new URLSearchParams(channelParams);

  return {
    name: params.get('name'),
    displayName: params.get('displayName'),
    provider: params.get('provider'),
    providerId: params.get('providerId')
  };
};

const parseNightbotUser = (userParams: string) => {
  const params = new URLSearchParams(userParams);

  return {
    name: params.get('name'),
    displayName: params.get('displayName'),
    provider: params.get('provider'),
    providerId: params.get('providerId'),
    userLevel: params.get('userLevel')
  };
};

进入全屏模式 退出全屏模式

更新 ping.ts API 函数以显示用户名和频道现在相对简单!

// ping.ts

export default async function (req: NextApiRequest, res: NextApiResponse) {
  const channel = parseNightbotChannel(
    req.headers['nightbot-channel'] as string
  );

  const user = parseNightbotUser(req.headers['nightbot-user'] as string);

  res
    .status(200)
    .send(
      `Hello! Your username is ${user.displayName} and the current channel is ${channel.displayName}.`
    );
}

进入全屏模式 退出全屏模式


✅让我们测试一下

我们的端点已经构建,但是我们将如何为它构建单元测试呢?你会在下面看到这并不太难。请注意,Jest 默认不附带新的 Next.js 项目但设置很简单。

添加测试开发依赖

为了让生活不那么痛苦,我建议安装node-mocks-http库:

npm i node-mocks-http --save-dev

OR

yarn add -D node-mocks-http

进入全屏模式 退出全屏模式

如果您是 Express.js 的普通用户,您可能熟悉使用supertest测试 API 端点。不幸的是,supertest无法帮助我们处理 Next.js 无服务器 API 路由。

创建测试文件

您的自然倾向可能是将 ping.test.ts 文件放在与 ping.ts 相同的目录中。这是一个很好的模式,但是由于 Next.js 的基于文件夹的路由是如何工作的,这不是一个好主意,因为 Vercel 会尝试部署你的测试😱

我建议在项目的根目录下创建一个__tests__文件夹,可以在其中对 pages 中的任何内容进行测试。在__tests__内部,创建一个包含 ping.test.tsapi 文件夹。

ping.test.ts 内 tests/api

编写测试

从这里构建测试代码非常简单:

import { createMocks } from 'node-mocks-http';

import ping from '../../pages/api/ping';

describe('Api Endpoint: ping', () => {
  it('exists', () => {
    // Assert
    expect(ping).toBeDefined();
  });

  it('responds with details about the user and channel', async () => {
    // Arrange
    const { req, res } = createMocks({
      method: 'GET',
      headers: {
        'nightbot-channel':
          'name=kongleague&displayName=KongLeague&provider=twitch&providerId=454709668',
        'nightbot-user':
          'name=wescopeland&displayName=WesCopeland&provider=twitch&providerId=52223868&userLevel=moderator'
      }
    });

    // Act
    await ping(req, res);
    const resData = res._getData();

    // Assert
    expect(resData).toContain('Your username is WesCopeland');
    expect(resData).toContain('the current channel is KongLeague');
  });
});

进入全屏模式 退出全屏模式


🤖 最后,设置 Nightbot

转到 Nightbot 网站,注册,然后单击 Nightbot 仪表板中的“加入频道”按钮。 Nightbot 现在将出现在您的 Twitch(或 YouTube!)频道上。

我假设您已经在某个地方部署了 Next.js 项目。 您应该能够在浏览器中访问新创建的ping路由。如果您是新手,那么对于 Next.js 项目来说,部署到 Vercel 可能是最简单的。只需注册,指向您的 GitHub 存储库,然后单击部署即可。

现在 Nightbot 在您的 Twitch 频道中,请转到您在 Twitch 上的聊天。通过在聊天中输入来创建一个新的 Nightbot 命令:

!commands add !ping $(urlfetch https://YOUR_URL/api/ping)

进入全屏模式 退出全屏模式

完成此操作后,Nightbot 应回复说命令已添加。您现在应该可以在聊天中输入“!ping”并查看您的 API 响应!你都准备好了!


🔒 不要忘记安全

任何人只需使用“!commands”即可访问 Nightbot 的 Twitch 频道命令列表。 Nightbot 隐藏 API 路由地址,将它们视为机密或环境变量,但是任何知道您的端点地址的人都可以模拟标头并假装他们不在 Postman 或 Insomnia 中的人。

换句话说,如果您想将聊天命令的发起者视为“经过身份验证”,则需要另一层安全性。

如果这对您很重要(在涉及通道点或用户角色等高级用例中很典型),我建议向您的端点添加代码,以确保 API 调用实际上来自 Twitch 或 Nightbot 本身。可以在 API 调用的请求标头中检查这一点。


👋 就是这样!

感谢您的阅读,希望这对那里的人有所帮助!如果您对我未来的任何内容感兴趣,请务必在 dev.to 上关注我。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐