发布于 2026-01-05 4 阅读
0

使用 ChatGPT、React 和 NodeJS 构建全球最大的通知库🤯 TLDR;

使用 ChatGPT、React 和 NodeJS 构建全球最大的通知库🤯

TLDR;

TLDR;

我将向您展示如何创建一个类似本网站上的通知库:
https://notifications.directory

我将向您展示如何使用 ChatGPT 创建数千条通知,并使用 React 进行渲染。

如果你能点击“喜欢”按钮支持我,我会非常开心,这能帮助我创作更多文章!

爱

你需要知道的事

目前 ChatGPT API 尚不可用。OpenAI
的最新模型是“text-davinci-003”
如果您不介意使用旧模型,不妨试一试!

但请记住,它不如当前的 ChatGPT 模型“text-davinci-002-render”先进。在本教程中,我将向您展示一个由开源社区成员创建的解决方案。

目前这不失为一个好办法,但请注意,它可能不会一直可用。OpenAI 尚未发布 ChatGPT API,因此这只是一个临时解决方案,不能用于商业用途。

表情包

我们需要哪些数据?

我将教你如何简化地完成我们在通知生成器项目中所做的同样的事情。

我们将创建三个层级结构。

  • 网站类型分类
    • SaaS
    • 电子商务
    • 在线市场
    • 旅行
    • ETC..
  • 通知类型
    • SaaS:“付款到期”
    • 电子商务:“产品缺货”等……
  • 通知
    • “您好,[姓名],您的款项将于[日期]到期。”
    • “您好,NAME,产品PRODUCT_NAME已售罄。”

要创建每个层级结构,我们必须拥有前一个层级结构的数据。

在 Novu 中,我们将所有内容都存储在 MongoDB 中;而在这里,我们将使用一个简单的数组来完成所有操作。

Novu——首个开源通知基础设施

简单介绍一下我们。Novu 是首个开源通知基础设施。我们主要帮助用户管理所有产品通知,包括应用内通知(类似 Facebook 的铃铛图标)、电子邮件、短信等等。

诺武

如果您能给我们点个赞,我会非常开心!也请在评论区告诉我哦❤️
https://github.com/novuhq/novu

注册 ChatGPT

请访问https://platform.openai.com/signup

注册 OpenAI,请访问此端点:https: //chat.openai.com/api/auth/session

从 JSON 中获取 accessToken。

访问令牌

项目设置

我们将从创建项目和创建主文件开始。

mkdir backend
cd backend
npm init
touch index.js
Enter fullscreen mode Exit fullscreen mode

让我们安装 chatgpt 库。

npm install @waylaidwanderer/chatgpt-api --save
Enter fullscreen mode Exit fullscreen mode

这个库使用的是ESM而不是CommonJS。因此,如果您尝试用NestJS来实现它,可能会遇到问题——如果您需要帮助,请在评论区留言。

为了本教程的简洁性,我不会将代码拆分成多个文件。打开 index.js 文件,我们开始编写代码。

让我们启动 ChatGPT。如您所见,该库使用的是  chatgpt.hato.ai* ”, 这是社区成员找到的一个使用 ChatGPT 的解决方案,他慷慨地将其部署在了自己的服务器上。使用此方案时,您的 ChatGPT 帐户将暴露给外部资源,因此请确保不要添加任何信用卡信息,并且您使用的是免费版 ChatGPT。
**请注意,API 每秒最多只能处理 10 个请求。
**

import { ChatGPTClient } from '@waylaidwanderer/chatgpt-api'

const api = new ChatGPTClient('YOUR ACCESS TOKEN FROM THE PREVIOUS STEP', {
  reverseProxyUrl: 'https://chatgpt.hato.ai/completions',
    modelOptions: {
      model: 'text-davinci-002-render',
  },
});
Enter fullscreen mode Exit fullscreen mode

我们来创建数据库吧🤣

const categories = []
Enter fullscreen mode Exit fullscreen mode

“类别”的最终结果将如下所示

[
  {
    "category": "SaaS",
    "notificationTypes": [
      {
        "name": "Payment is due",
        "notifications": [
          "Hi {{name}}, your payment is due for. {{date}}"
        ]
      }
    ]
  }
]
Enter fullscreen mode Exit fullscreen mode

你的代码现在应该看起来像这样:

import { ChatGPTClient } from '@waylaidwanderer/chatgpt-api'

const api = new ChatGPTClient('YOUR ACCESS TOKEN FROM THE PREVIOUS STEP', {
  reverseProxyUrl: 'https://chatgpt.hato.ai/completions',
    modelOptions: {
      model: 'text-davinci-002-render',
  },
});

const notifications = [];
Enter fullscreen mode Exit fullscreen mode

让我们编写生成所有类别的代码。

我们希望提示和结果看起来像这样:

聊天1

问题在于,所有数据都会以纯文本的形式传入。
我们需要一个函数来从列表中创建一个数组。
我已经写了一个简单的函数。
如果你有更高效的方法,请在评论区告诉我:

const extractNumberedList = (text) => {
    return text.split("\n").reduce((all, current) => {
        const values = current.match(/\d+\.(.*)/);
        if (values?.length > 1) {
            return [...all, values[1].trim()];
        }

        return all;
    }, []);
}
Enter fullscreen mode Exit fullscreen mode

现在到了有趣的部分。
我们需要所有可能的类别,然后通过我们的函数运行它们,并将它们映射到我们的数据库中:

const res = await api.sendMessage('Can you list 50 types of websites in the world send in-app notifications? just the category');
categories.push(...extractNumberedList(res.response).map(p => ({category: p, notificationTypes: []})));
Enter fullscreen mode Exit fullscreen mode

现在我们将遍历这些类别并开始向其中添加内容。第一部分我不会使用函数式编程,而是
使用“for 循环”,因为这样配合 async/await 会更方便:

for (const category of categories) {

}
Enter fullscreen mode Exit fullscreen mode

让我们开始创建通知类型:

通知类型

for (const category of categories) {
    const notificationTypesRes = await api.sendMessage(`I have a website of type ${category}, What kind of notifications should I sent to my users? can you just write the type without context? give me 20`);
  const notificationTypes = extractNumberedList(res.response).map(p => ({name: p, notifications: []}));
}
Enter fullscreen mode Exit fullscreen mode

现在,让我们添加通知功能:

通知

for (const category of categories) {
  // get all the notifications type
    const notificationTypesRes = await api.sendMessage(`I have a website of type ${category}, What kind of notifications should I sent to my users? can you just write the type without context? give me 20`);
  // parse all the notification type and map them
  const notificationTypes = extractNumberedList(notificationTypesRes.response).map(p => ({name: p, notifications: []}));
  // get all the notifications for the notification type
  const notifications = await Promise.all(notificationTypes.map(async p => {
        const notificationRes = await api.sendMessage(`I have built a system about "${category}" and I need to create in-app notifications about "${notificationType}", can you maybe write me a 20 of those? just the notification without the intro, use lower-case double curly braces with no spaces and underscores for the variables, and avoid using quotation when writing the notifications`);
        return {
            ...p,
            notifications: extractNumberedList(notificationRes.response)
        }
    }));
  // push it to the main array
    notificationTypes.notifications.push(...notifications);
}
Enter fullscreen mode Exit fullscreen mode

最终代码如下所示:

import { ChatGPTClient } from '@waylaidwanderer/chatgpt-api'

const api = new ChatGPTClient('YOUR ACCESS TOKEN FROM THE PREVIOUS STEP', {
  reverseProxyUrl: 'https://chatgpt.hato.ai/completions',
    modelOptions: {
      model: 'text-davinci-002-render',
  },
});

const categories = [];

const extractNumberedList = (text) => {
    return text.split("\n").reduce((all, current) => {
        const values = current.match(/\d+\.(.*)/);
        if (values?.length > 1) {
            return [...all, values[1].trim()];
        }

        return all;
    }, []);
}

const res = await api.sendMessage('Can you list 50 types of websites in the world send in-app notifications? just the category');
categories.push(...extractNumberedList(res.response).map(p => ({category: p, notificationTypes: []})));

for (const category of categories) {
  // get all the notifications type
    const notificationTypesRes = await api.sendMessage(`I have a website of type ${category}, What kind of notifications should I sent to my users? can you just write the type without context? give me 20`);
  // parse all the notification type and map them
  const notificationTypes = extractNumberedList(notificationTypesRes.response).map(p => ({name: p, notifications: []}));
  // get all the notifications for the notification type
  const notifications = await Promise.all(notificationTypes.map(async p => {
        const notificationRes = await api.sendMessage(`I have built a system about "${category}" and I need to create in-app notifications about "${notificationType}", can you maybe write me a 20 of those? just the notification without the intro, use lower-case double curly braces with no spaces and underscores for the variables, and avoid using quotation when writing the notifications`);
        return {
            ...p,
            notifications: extractNumberedList(notificationRes.response)
        }
    }));
  // push it to the main array
    notificationTypes.notifications.push(...notifications);
}
Enter fullscreen mode Exit fullscreen mode

它可以一直后台运行🥳

我们可以很快地将其移至单独的任务/定时任务/队列,但我们不会在这里这样做。

在后台运行这个程序的同时,我们来添加前端可以使用的 API。

让我们回到命令行,输入:

npm install express --save
Enter fullscreen mode Exit fullscreen mode

我们将采取最简单的做法,只提供我们的类别变量。

您可以尝试使用实际数据库,将其提升到更高层次:


import express from 'express';
const app = express()
const port = 3000

app.get('/', (req, res) => {
  return categories;
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})
Enter fullscreen mode Exit fullscreen mode

完整的代码如下:


import { ChatGPTClient } from '@waylaidwanderer/chatgpt-api';
import express from 'express';

const categories = [];
const app = express();
const port = 3000;

const api = new ChatGPTClient('YOUR ACCESS TOKEN FROM THE PREVIOUS STEP', {
  reverseProxyUrl: 'https://chatgpt.hato.ai/completions',
    modelOptions: {
      model: 'text-davinci-002-render',
  },
});

const extractNumberedList = (text) => {
    return text.split("\n").reduce((all, current) => {
        const values = current.match(/\d+\.(.*)/);
        if (values?.length > 1) {
            return [...all, values[1].trim()];
        }

        return all;
    }, []);
}

app.get('/', (req, res) => {
  return categories;
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
});

const res = await api.sendMessage('Can you list 50 types of websites in the world send in-app notifications? just the category');
categories.push(...extractNumberedList(res.response).map(p => ({category: p, notificationTypes: []})));

for (const category of categories) {
  // get all the notifications type
    const notificationTypesRes = await api.sendMessage(`I have a website of type ${category}, What kind of notifications should I sent to my users? can you just write the type without context? give me 20`);
  // parse all the notification type and map them
  const notificationTypes = extractNumberedList(notificationTypesRes.response).map(p => ({name: p, notifications: []}));
  // get all the notifications for the notification type
  const notifications = await Promise.all(notificationTypes.map(async p => {
        const notificationRes = await api.sendMessage(`I have built a system about "${category}" and I need to create in-app notifications about "${notificationType}", can you maybe write me a 20 of those? just the notification without the intro, use lower-case double curly braces with no spaces and underscores for the variables, and avoid using quotation when writing the notifications`);
        return {
            ...p,
            notifications: extractNumberedList(notificationRes.response)
        }
    }));
  // push it to the main array
    notificationTypes.notifications.push(...notifications);
}
Enter fullscreen mode Exit fullscreen mode

NODE.JS ✅

现在让我们切换到响应式布局,以显示我们所有的类别。

我将采用最简单的方法,把所有内容汇总到一个……<ul>

让我们来编写一些 bash 命令:

cd ..
npx create-react-app frontend
cd frontend
Enter fullscreen mode Exit fullscreen mode

我将使用 axios 从数据库中获取所有结果,您也可以随意使用 fetch / react-query 等工具。

npm install axios
Enter fullscreen mode Exit fullscreen mode

让我们打开位于“src/App.js”的主组件,并添加一些导入语句。

import {useState, useCallback, useEffect} from 'react';
import axios from 'axios';
Enter fullscreen mode Exit fullscreen mode

让我们打开主组件“src/App.js”,并添加一个新的状态,该状态将包含来自服务器的数据库的所有内容:

const [categories, setCategories] = useState([]);
Enter fullscreen mode Exit fullscreen mode

现在,我们将编写一个函数,用于从服务器获取所有信息。

const serverInformation = useCallback(async () => {
    const {data} = await axios.get('http://localhost:3000');
    setCategories(data);
}, []);
Enter fullscreen mode Exit fullscreen mode

将其添加到我们的 useEffect 中,它会在组件加载后立即运行。

useEffect(() => {
    serverInformation();
}, []);
Enter fullscreen mode Exit fullscreen mode

现在,我们将把所有内容渲染到屏幕上。

<ul>
    {categories.map(cat => (
        <li>
            {cat.category}
            <ul>
                {cat.notificationTypes.map(notificationType => (
                    <li>
                        {notificationType.name}
                        <ul>
                            {notificationType.notifications.map(notification => (
                                <li>
                                    {notification}
                                <li>
                            )}
                        </ul>
                    <li>
                )}
            </ul>
        <li>
    )}
</ul>
Enter fullscreen mode Exit fullscreen mode

好了!我们已经渲染完所有列表了!!!

App.js 的完整代码如下所示:

import {useState, useCallback, useEffect} from 'react';
import axios from 'axios';

function App() {
    const [categories, setCategories] = useState([]);

    useEffect(() => {
        serverInformation();
    }, []);

    const serverInformation = useCallback(async () => {
        const {data} = await axios.get('http://localhost:3000');
        setCategories(data);
    }, []);

    return (
        <ul>
            {categories.map(cat => (
                <li>
                    {cat.category}
                    <ul>
                        {cat.notificationTypes.map(notificationType => (
                            <li>
                                {notificationType.name}
                                <ul>
                                    {notificationType.notifications.map(notification => (
                                        <li>
                                            {notification}
                                        <li>
                                    )}
                                </ul>
                            <li>
                        )}
                    </ul>
                <li>
            )}
        </ul>
    );
}
Enter fullscreen mode Exit fullscreen mode

当然,这是前端的简短版本。

您可以在这里找到完整的前端代码(以及精美的设计):
https://github.com/novuhq/notification-directory-react

截屏

希望你有所收获。下次再见😎

帮帮我!

如果你觉得这篇文章对你有帮助,请给我们点个赞,我会非常开心!也欢迎在评论区留言告诉我哦❤️

https://github.com/novuhq/novu

文章来源:https://dev.to/novu/building-the-largest-notifications-library-in-the-world-using-chatgpt-react-and-nodejs-2of7