使用 Notion 和 Kelvin Data 实现个人 CRM 自动化

2025-06-05

使用 Notion 和 Kelvin Data 实现个人 CRM 自动化

介绍

Notion 为我们用单个应用程序实现的功能提供了巨大的可能性,老实说,它是管理个人 CRM 的最佳方式之一。

现在,如果有一种方法可以尝试从网上获取诸如 twitter id、Linkedin url、姓名和其他一些详细信息,只需提供用户电子邮件地址即可。

感觉很棒吧?

了解一下Kelvin Data,它是一种 API 即服务,开发人员可以使用 API 访问数百万人的数据。

先决条件

  • JavaScript 基础
  • 节点 JS
  • Notion 账户
  • 开尔文数据账户

我们将要构建什么

https://i.imgur.com/6JzIGJq.png

https://p-843661.f1.n0.cdn.getcloudapp.com/items/YEuObYbQ/7bde6b36-74d5-4e58-9768-168708b2061f.gif?v=3b53d2ff7791536114bbb7f52a3f2380

上面的 GIF 展示了 API 的工作原理,它将为我们获取并填充剩余字段的数据。字段的填充由 Notion Integration 负责。

让我们一起建造

https://media4.giphy.com/media/YKLLS6ZGQsrwZVgXix/giphy.gif

概念整合

要运行任何自动化并访问 Notion API,我们需要一些称为 Notion 集成的东西。

您可以前往https://www.notion.so/my-integrations创建您的集成并创建您的 Notion 集成。

万一您在任何地方遇到意外,请使用此处提到的详细指南。

一旦完成集成创建,它看起来就会像这样。

https://i.imgur.com/mh0MYDh.png

KelvinData是我的集成的名称,下一部分我们将需要内部集成令牌。

创建概念数据库

转到任何 Notion 页面并单击/并搜索Table Full Page选项,然后单击输入。]

您可以在下面生成的 GIF 中找到这些步骤。

https://p-843661.f1.n0.cdn.getcloudapp.com/items/BluxPAoq/28e0f6cd-68e2-493e-b366-632cba813db3.gif?v=071f8beb6d51f31a916503ce6e6414cd

现在您需要获取我们创建的数据库的数据库 ID,您可以按照此处提到的步骤获取该 ID https://developers.notion.com/docs#step-2-share-a-database-with-your-integration

将集成添加到创建的 Notion 数据库

现在,一旦我们有了数据库,我们就必须授予我们在第一步中完成的 Notion 集成的访问权限。

您可以在下面生成的 GIF 中找到这些步骤。

https://p-843661.f1.n0.cdn.getcloudapp.com/items/OAunvy8W/71524d66-99cc-41e7-bc1b-620ca32e1f2f.gif?v=aaf5b5a9b4705eeb13036d85af2f3b9c

您现在可以添加所需的列,对于我们要做的示例,我们将添加

  • 姓名
  • 叽叽喳喳
  • LinkedIn
  • 电子邮件
  • 杂项

您可以根据需要添加许多其他字段。

我的表格行看起来像这样

https://i.imgur.com/o6ZC8t4.png

Node JS 应用程序

对于此应用程序,我们将使用 JS 来构建 Node JS 应用程序。

创建应用程序

执行以下命令来创建项目并安装所需的依赖项和开发依赖项。

mkdir notion-crm-kelvindata # Creates new directory
cd notion-crm-kelvindata # Moves to the created directory

npm init -y # Initialises the basic npm app

npm install @notionhq/client api dotenv # Installing the required dependencies
npm install --save-dev nodemon # Installing the required dev dependencies
Enter fullscreen mode Exit fullscreen mode

编辑 package.json 文件

编辑package.json文件并添加以下行

"type": "module",

这将确保我们可以进行 ES6 导入。

在该scripts部分中,添加以下脚本

"dev": "nodemon index.js"

这将不断监听变化并运行应用程序。

完成后,package.json文件看起来像这样。

{
  "name": "notion-crm-kelvindata",
  "version": "1.0.0",
  "description": "",
  "type": "module",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon index.js"
  },
  "keywords": [],
  "author": "Rohith Gilla",
  "license": "ISC",
  "dependencies": {
    "@notionhq/client": "^0.3.2",
    "api": "^3.4.0",
    "dotenv": "^10.0.0"
  },
  "devDependencies": {
    "nodemon": "^2.0.12"
  }
}
Enter fullscreen mode Exit fullscreen mode

环境文件

我们将需要NOTION_DATABASE_IDNOTION_SECRET_KEYKELVIN_DATA_KEY

我们已经在第一个 Notion 集成设置中看到了如何获取数据库 ID 和密钥。

现在我们需要很棒的 Kelvin Data API 密钥,很简单,访问https://www.kelvindata.com/并单击获取 API 密钥按钮,填写所需的详细信息即可完成。

将其保存在环境文件中,为了方便访问,我创建了一个.env.example文件。您可以在 GitHub 存储库中找到该文件,链接位于博客文章下方。

它看起来像这样,但您需要填写详细信息来代替字符串。

NOTION_DATABASE_ID=""
NOTION_SECRET_KEY=""
KELVIN_DATA_KEY=""
Enter fullscreen mode Exit fullscreen mode

由于我们将其用作模块来使用require关键字,因此我们需要通过以下方式定义 require。

import { createRequire } from "module";
const require = createRequire(import.meta.url);
Enter fullscreen mode Exit fullscreen mode

开尔文数据初始化

Kelvin Data 有很好的 API 参考,你可以在这里找到它https://kelvin-data.readme.io/reference/searchv2_query

它展示了如何集成各种框架和技术。

我们使用 Node,因此第一步我们需要初始化将用于搜索用户数据库的 kelvin data sdk。

const kelvinSDK = require("api")("@kelvin-data/v1.0#3bettnkt7yytde");
Enter fullscreen mode Exit fullscreen mode

这行代码会帮我们获取所需的 SDK,简单来说,这个包api会接受 OpenAPI 规范并生成SDK。这太酷了,也太实用了。

Notion API 初始化

import { Client } from "@notionhq/client";

const NOTION_SECRET_KEY = process.env.NOTION_SECRET_KEY;
const NOTION_DATABASE_ID = process.env.NOTION_DATABASE_ID;

const notion = new Client({ auth: NOTION_SECRET_KEY });
Enter fullscreen mode Exit fullscreen mode

查询

现在我们必须查询我们在步骤 1 中构建的 Notion 表。

const response = await notion.databases.query({
      database_id: NOTION_DATABASE_ID,
  });
Enter fullscreen mode Exit fullscreen mode

简单,我们直接用参数去数据库中查询database_id

我们可以向 传递多个参数filtersort并指定页面大小。本例中我们尽量简单,只传递database_id

获取必填字段

现在我们需要所有列对象,更重要的是电子邮件字段上的文本。

const email = result.properties["Email"];
const name = result.properties["Name"];
const emailText = email[email.type][0]["plain_text"];
const isAdded = result.properties["Added"];
const isAddedBool = isAdded[isAdded.type];
const linkedIn = result.properties["LinkedIn"];
const misc = result.properties["Misc"];
const twitter = result.properties["Twitter"];
Enter fullscreen mode Exit fullscreen mode

变量email name isAdded linkedIn twittermisc包含相应字段的值。

它们是有点疯狂的嵌套对象!!

const emailText = email[email.type][0]["plain_text"];

通过上述操作,我们得到了电子邮件的文本。

如果您看到数据库中有一个字段Added,即复选框。这将帮助我们了解该行是否已被处理。

为了获取字段的值,我们执行的操作与获取字段的值时所做的事情类似。

const isAdded = result.properties["Added"];

用数据初始化变量

var fullName = "Not Found";
var linkedInUrl = "Not Found";
var twitterUrl = "Not Found";
var miscData = "Not Found";
Enter fullscreen mode Exit fullscreen mode

这些是我们想要在用例中找出的有关人员的数据,我们将用“未找到”值预先填充它们,一旦找到,我们将用实际值替换。

搜索并保存

if (!isAddedBool) {
    // Search and save logic
}
Enter fullscreen mode Exit fullscreen mode

首先,我们检查复选框值是否为真,这意味着该行已经被处理。

点击 Kelvin Data API 并获取结果

const searchResponse = await kelvinSDK["searchV2_query"]({
  email: emailText,
  limit: 1,
});
Enter fullscreen mode Exit fullscreen mode

由于 SDK 为我们生成了所有内容,我们只需要使用以下命令查询 APIemail

有不同的方法来查询 API,您可以在这里找到它们。

现在是应用程序最简单的部分,从响应中获取所需字段并将它们保存在我们上面创建的变量中。

if (searchResponse.length !== 0) {
    fullName = searchResponse[0].name.full;
    const linkedInObj = searchResponse[0].profiles.find(
      (profile) => profile.network === "linkedin"
    );
    const twitterObj = searchResponse[0].profiles.find(
      (profile) => profile.network === "twitter"
    );
    if (linkedInObj) {
      linkedInUrl = linkedInObj.url;
    }
    if (twitterObj) {
      twitterUrl = twitterObj.url;
    }
  }
Enter fullscreen mode Exit fullscreen mode

Notion API 更新

Notion API 更新的工作方式并未针对所有用例进行正确记录,API 文档仅讨论如何更新布尔值或数字。它们没有讨论如何更新文本或其他字段。

经过一番挖掘,我发现了以下更新方法,请注意,这可能会在未来的版本中发生变化,但大致相似。

我们需要构建一个对象来更新字段,可以通过以下方式完成。

var changedResult = {
    ...result.properties,
    Twitter: {
      ...twitter,
      rich_text: [
        {
          type: "text",
          text: {
            content: twitterUrl,
            link: twitterUrl !== "Not Found" ? { url: twitterUrl } : null,
          },
          plain_text: twitterUrl,
          href: null,
        },
      ],
    },
    LinkedIn: {
      ...linkedIn,
      rich_text: [
        {
          type: "text",
          text: {
            content: linkedInUrl,
            link:
              linkedInUrl !== "Not Found" ? { url: linkedInUrl } : null,
          },
          plain_text: linkedInUrl,
          href: null,
        },
      ],
    },
    Misc: {
      ...misc,
      rich_text: [
        {
          type: "text",
          text: { content: miscData, link: null },
          plain_text: miscData,
          href: null,
        },
      ],
    },
    Added: {
      ...isAdded,
      checkbox: true,
    },
    Name: {
      ...name,
      title: [
        {
          type: "text",
          text: { content: fullName, link: null },
          plain_text: fullName,
          href: null,
        },
      ],
    },
  };
Enter fullscreen mode Exit fullscreen mode

让我们检查一下物体,看看发生了什么

LinkedIn: {
    ...linkedIn,
    rich_text: [
      {
        type: "text",
        text: {
          content: linkedInUrl,
          link:
            linkedInUrl !== "Not Found" ? { url: linkedInUrl } : null,
        },
        plain_text: linkedInUrl,
        href: null,
      },
    ],
  },
Enter fullscreen mode Exit fullscreen mode
  • ...linkedIn我们正在传播初始值,因为它们包含一些字段,例如id和其他字段。
  • 我们需要覆盖该rich_text字段,为了实现这一点,我们通过将以下对象添加到该rich_text数组来执行以下操作。
{
    type: "text", // type of the value
    text: {
      content: linkedInUrl,
      link:
        linkedInUrl !== "Not Found" ? { url: linkedInUrl } : null,
    },
    plain_text: linkedInUrl,
    href: null,
  },
Enter fullscreen mode Exit fullscreen mode

我们对其他字段也进行同样的操作。

最后一步使用 Notion API 更新对象

这非常简单,我们获取更新的对象并使用概念 api 更新数据库。

await notion.pages.update({
  page_id: result.id,
  properties: changedResult,
});
Enter fullscreen mode Exit fullscreen mode

错误处理

我们将保持其简单,整个函数将被包装在一个try/catch块中。

定期运行

我们所拥有的功能需要每 5 秒定期运行一次,更像是一个 cron 作业,但又不是一个 cron 作业。

我们使用 JavaScriptsetTimeout函数来实现这一点。

setTimeout(main, 5000);
Enter fullscreen mode Exit fullscreen mode

把所有东西拼接在一起

现在让我们把我们做的所有东西放在一起👇

import { createRequire } from "module";
const require = createRequire(import.meta.url);
import { Client } from "@notionhq/client";

const kelvinSDK = require("api")("@kelvin-data/v1.0#3bettnkt7yytde");
require("dotenv").config();

const NOTION_SECRET_KEY = process.env.NOTION_SECRET_KEY;
const NOTION_DATABASE_ID = process.env.NOTION_DATABASE_ID;

kelvinSDK.auth(process.env.KELVIN_DATA_KEY);

const notion = new Client({ auth: NOTION_SECRET_KEY });

async function main() {
  try {
    const response = await notion.databases.query({
      database_id: NOTION_DATABASE_ID,
    });
    //iterate over response.results
    response.results.forEach(async (result) => {
      const email = result.properties["Email"];
      const name = result.properties["Name"];
      const emailText = email[email.type][0]["plain_text"];
      const isAdded = result.properties["Added"];
      const isAddedBool = isAdded[isAdded.type];
      const linkedIn = result.properties["LinkedIn"];
      const misc = result.properties["Misc"];
      const twitter = result.properties["Twitter"];

      var fullName = "Not Found";
      var linkedInUrl = "Not Found";
      var twitterUrl = "Not Found";
      var miscData = "Not Found";
      if (!isAddedBool) {
        const searchResponse = await kelvinSDK["searchV2_query"]({
          email: emailText,
          limit: 1,
        });
        if (searchResponse.length !== 0) {
          fullName = searchResponse[0].name.full;
          const linkedInObj = searchResponse[0].profiles.find(
            (profile) => profile.network === "linkedin"
          );
          const twitterObj = searchResponse[0].profiles.find(
            (profile) => profile.network === "twitter"
          );
          if (linkedInObj) {
            linkedInUrl = linkedInObj.url;
          }
          if (twitterObj) {
            twitterUrl = twitterObj.url;
          }
        }
        var changedResult = {
          ...result.properties,
          Twitter: {
            ...twitter,
            rich_text: [
              {
                type: "text",
                text: {
                  content: twitterUrl,
                  link: twitterUrl !== "Not Found" ? { url: twitterUrl } : null,
                },
                plain_text: twitterUrl,
                href: null,
              },
            ],
          },
          LinkedIn: {
            ...linkedIn,
            rich_text: [
              {
                type: "text",
                text: {
                  content: linkedInUrl,
                  link:
                    linkedInUrl !== "Not Found" ? { url: linkedInUrl } : null,
                },
                plain_text: linkedInUrl,
                href: null,
              },
            ],
          },
          Misc: {
            ...misc,
            rich_text: [
              {
                type: "text",
                text: { content: miscData, link: null },
                plain_text: miscData,
                href: null,
              },
            ],
          },
          Added: {
            ...isAdded,
            checkbox: true,
          },
          Name: {
            ...name,
            title: [
              {
                type: "text",
                text: { content: fullName, link: null },
                plain_text: fullName,
                href: null,
              },
            ],
          },
        };
        await notion.pages.update({
          page_id: result.id,
          properties: changedResult,
        });
      }
    });
  } catch (error) {
    console.log(error);
  }
  setTimeout(main, 5000);
}

main();
Enter fullscreen mode Exit fullscreen mode

GitHub 存储库

您可以在这里找到存储库。

GitHub - Rohithgilla12/notion-crm-kelvindata:

如果您喜欢它,请为该存储库加注星标。

部署解决方案

在这篇博文中,我们不会介绍部署,但会建议一些免费的替代方案

您可以使用 Deta Micros 在 Deta 上部署您的 Node JS 应用程序,您可以在这里了解更多信息

如果您对heroku更熟悉,您可以尝试一下。

您可以使用 vercel 来部署您的 NodeJS 应用程序。

运行时

您还可以查看StackBlitz

另一个值得一试的好资源是Replit

解决方案有很多,我仅列举了其中几个。

后续步骤

Notion 非常强大,而集成则使其更加强大。

Kelvin Data 是一个令人惊叹的 API,其创意无限,从个人 CRM 到企业级扩展等等。

继续航行,创造奇迹。

[更新 1]:感谢您对该帖子的积极回应,我已经与 Kelvin Data 团队进行了交谈,他们说在大约一周内,在实施防止滥用的系统后,将有一种方法可以通过优惠券代码获得免费积分。

因此,请继续关注文章中提供的优惠券代码,您将凭借该优惠券代码获得免费积分,或者查看我的Twitter,我将在那里更新相同的内容。

谢谢,

罗希特·吉拉

文章来源:https://dev.to/gillarohith/automate-your-personal-crm-with-notion-and-kelvin-data-ono
PREV
2019 年开发 Web 组件的 7 个工具
NEXT
我的副业项目如何成为我的全职创业项目!