如何用Zapier和JavaScript建立一个Slack机器人来获取Reddit上的流行话题

461 阅读10分钟

Reddit是一个了解网络开发新闻的好地方,如果你像我一样,你可能会关注r/node或r/javascript等子reddits。我最近发现了一个很好的方法,只用我的JavaScript知识就可以建立一个Zapier Reddit集成--这样我就可以在我的团队频道中分享那些流行的Reddit帖子。

在这篇文章中,你会学到。

  • 如何建立一个Slack机器人集成。
  • 如何在Zapier中创建触发器和行动工作流程,将信息发布到Slack频道。
  • 如何使用Reddit API密钥来访问帖子,使用本地Reddit访问令牌和刷新令牌。
  • 如何使用匿名JSON端点访问Reddit帖子。
  • 编写JavaScript代码(与Node.js版本10兼容,注意是End of Life),用node-fetch HTTP客户端来获取Reddit信息。

你能用Slack机器人做什么?

本教程教你如何用JavaScript编写Slack机器人,但Zapier功能丰富的API生态系统允许你将许多其他信息源连接到Slack机器人的API。虽然这个Slack机器人教程侧重于JavaScript,但如果你把你的Node.js应用程序托管在云端,你也可以用Zapier的HTTP网络钩子编写一个Slack机器人Node.js应用程序。

构建一个Zapier Reddit集成来处理新的趋势性话题

什么是Zapier?

Zapier是一个自动化平台,它允许你与许多其他平台集成,以连接它们并产生一个工作流程。它主要用于API和原生集成,当这些在Zapier市场上可用时,但它进一步闪耀,为你提供集成点的原始输入和输出,并允许你通过代码定制工作流程的过程。

为了促进Slack机器人的集成,我们将使用Zapier,它已经有了自己与Slack的原生集成--这意味着我们不需要将我们的机器人托管在专用服务器中,也不需要创建另一个单独的Slack机器人集成。

既然我们有了Slack机器人与Zapier的集成,我们的下一步将是。

  1. 定义一个集成运行的时间表。这可以是一个经常性的时间表,其中Zapier任务将启动并开始处理。
  2. 在Zapier中定义一个自定义的JavaScript代码任务,从Reddit的JSON端点获取我们感兴趣的趋势话题。
  3. 根据我们的喜好对趋势话题进行格式化,并通过Zapier内置的原生Slack机器人集成,将其发送到Slack频道。

在Zapier的工作流程界面中,整个过程看起来像。

Zapier workflow to build a slack bot automation that fetch Reddit trending topics and sends them to a Slack channel.

触发器:Zapier的Schedule中的每周

我们首先要为Zapier工作流程设置循环时间表。具体来说,我选择了每周一在UTC时间上午8点运行它。

Every Week in Schedule by Zapier to define a trigger for our Slack bot integration.

如何实现Slack消息的自动化?

在这个Slack机器人教程中,我们选择了使用由Zapier触发的及时计划。然而,Zapier API平台有许多其他触发器,可以驱动Slack自动化。一些例子是。Twitter提及、Google Sheets更新、Google日历事件更新、RSS订阅更新,以及其他一些你可以插入Slack机器人的JavaScript自动化。你甚至可以将Snyk安全通知自动化,用于你的团队的漏洞追踪

行动:通过Zapier在代码中运行JavaScript

现在我们已经定义了基于时间的触发器来启动工作流程,我们可以开始创建行动来执行一些任务。Zapier集成允许我们连接从这些任务中处理的数据,并将其送入链中的下一个动作。

选择一个Run JavaScript in Code by Zapier 的行动类型。在Event 配置中,选择Run JavaScript 。最初的设置应该如下。

Run JavaScript in Code by Zapier.

然后,展开设置行动配置,在这里我们将添加相关的JavaScript代码,从Reddit API端点获取我们的趋势主题的信息。

如何使用Reddit API密钥

有几种方法来检索Reddit新闻。我们将介绍两种方法来接收有趣的Reddit线程的更新。

  1. 使用Reddit API密钥,这需要注册一个Reddit用户账户。
  2. 使用一个公开可用的Reddit API端点。

如果你只需要读取subreddits和趋势帖子,请使用更简单的工作流程,不涉及Reddit oAuth API工作流程--并跳到本章Reddit API访问的第二部分。

注册Reddit API密钥

首先导航到Reddit API应用偏好,点击屏幕底部的Create an app 。然后,输入你的API使用案例的信息。你需要将应用程序的类型设置为webapp ,并提供一个重定向URI来处理访问令牌。

Reddit oauth application creation screen to generate a Reddit API token

然后你将收到你的ClientIDClientSecret ,你可以用它来进行API调用。如果你不确定如何查询Reddit API和通过Reddit oAuth工作流程,请查看其他入门教程,如关于搜刮Reddit的这篇教程。

值得注意的是,你应该通读Reddit的API访问条款,以熟悉你必须满足的Reddit API规则和条件。

Reddit API的JavaScript例子

我们将专注于使用JavaScript来获取有趣的Reddit线程。要做到这一点,我们需要上面提到的ClientIDClientSecret 凭证,以及refresh_token 值,如果你选择了创建一个永久性的Reddit oAuth访问令牌,那么这个值就存在。

下面是一个Reddit API密钥的好例子,它可以使用本地JavaScript代码查询subreddit帖子。用上面提到的Reddit凭证替换下面的ABCD 文本,以使代码片段正常运行。

js
  // start -- get access token
   const clientId = ‘ABCD’;
   const clientSecret = ABCD’;

   const dataSecrets = `${clientId}:${clientSecret}`;
   const text = Buffer.from(dataSecrets).toString("base64");

   var url = new URL('https://www.reddit.com/api/v1/access_token');
   var params = {grant_type: 'refresh_token', refresh_token: ‘ABCD'};

   url.search = new URLSearchParams(params).toString();

    const settings = {
        method: 'POST',
        headers: {
            'User-Agent': 'query-api 1.0',
            'Accept': 'application/json',
            'Authorization': `Basic ${text}`,
        },
        credentials: 'omit'
    };

    const res = await fetch(url.href, settings);
    const body = await res.json();
    const accessToken = body.access_token;

    // -----------------------------------------------------
    // make reddit api request
    const params2 = {'limit': 3, 't': 'week'};
    const url2 = new URL('https://oauth.reddit.com/r/node/top');
    url2.search = new URLSearchParams(params2).toString();

    const settings2 = {
        method: 'GET',
        headers: {
            'User-Agent': 'query-api 1.0',
            'Accept': 'application/json',
            'Authorization': `Bearer ${accessToken}`,
        },
        credentials: 'omit'
    };

    const res2 = await fetch(url2.href, settings2);
    const body2 = await res2.json();

    let data = [];
    let message = `Top Node.js posts this week:\n\n`;

    const topPosts = body2.data.children;
    topPosts.forEach((post) => {
        data.push({
          link: post.data.url,
          redditLink: post.data.permalink,
          text: post.data.title,
          score: post.data.score,
          replies: post.data.num_comments
        });

        const text = post.data.title;
        const redditLink = post.data.permalink;

        message = message + `<https://reddit.com${redditLink}|${text}> \n`;
    });

    return {id: 1, message: message};

这个Reddit API的JavaScript代码片段使用refresh_token 来查询Reddit API并请求access_token 。一旦我们获得Reddit API的访问令牌,我们就可以创建一个新的HTTP请求--它可以获取提交到r/node 子reddit的趋势Reddit帖子。最后,我们循环浏览所有的帖子数据,并创建一个信息字符串,将其传递给我们的下一个动作。

从前面的代码片断中可以看到两个重要的观察。

  1. 你会注意到,正确的、最新的查询Reddit数据的API是使用t 中使用的查询参数params2 。然而,一些过时的教程和指南仍然提到使用time 来代替。

  2. 另一个有趣的地方是,由于Zapier限制我们使用旧版本的node-fetch ,运行在Node.js运行时间的终结版(Node.js v10)上,你实际上可以直接使用URL对象来进行HTTP请求。

js
    const url2 = new URL('https://oauth.reddit.com/r/node/top');
    const res2 = await fetch(url2, settings2);

用公共JSON端点查询Reddit信息

在继续其他JavaScript Zapier动作之前,让我们快速介绍一下如何查询Reddit的API,而不需要使用oAuth认证,或任何其他类型的API密钥。

Reddit使用以下端点以JSON格式提供subreddit线程信息,该端点是公开的,没有经过认证。

sh
https://www.reddit.com/r/node/top.json

你甚至可以通过添加以下查询参数,请求该子红点的热门帖子,并提供一个时间框架。

sh
https://www.reddit.com/r/node/top.json?limit=1&t=month

来自Reddit的JSON响应将是类似于以下的内容。

json
{
  "kind": "Listing",
  "data": {
    "after": "t3_vuyxes",
    "dist": 1,
    "modhash": "6v201d03619751a685b19dcf05febbf88ed6d115afa5d16b78",
    "geo_filter": "",
    "children": [
      {
        "kind": "t3",
        "data": {
          "approved_at_utc": null,
          "subreddit": "node",
          "selftext": "",
          "author_fullname": "t2_7eb9a4k5",
          "saved": false,
          "mod_reason_title": null,
          "gilded": 0,
          "clicked": false,
          "title": "Deno in 2022",
          "link_flair_richtext": [],
          "subreddit_name_prefixed": "r/node",
          "hidden": false,
          "pwls": 6,
          "link_flair_css_class": null,
          "downs": 0,
          "top_awarded_type": null,
          "hide_score": false,
          "name": "t3_vuyxes",
          "quarantine": false,
          "link_flair_text_color": "dark",
          "upvote_ratio": 0.92,
          "author_flair_background_color": null,
          "subreddit_type": "public",
          "ups": 745,
          "total_awards_received": 0,
          "media_embed": {},
          "author_flair_template_id": null,
          "is_original_content": false,
          "user_reports": [],
          "secure_media": null,
          "is_reddit_media_domain": true,
          "is_meta": false,
          "category": null,
          "secure_media_embed": {},
          "link_flair_text": null,
          "can_mod_post": false,
          "score": 745,
          "approved_by": null,
          "is_created_from_ads_ui": false,
          "author_premium": false,
          "thumbnail": "",
          "edited": false,
          "author_flair_css_class": null,
          "author_flair_richtext": [],
          "gildings": {},
          "content_categories": null,
          "is_self": false,
          "mod_note": null,
          "created": 1657363081,
          "link_flair_type": "text",
          "wls": 6,
          "removed_by_category": null,
          "banned_by": null,
          "author_flair_type": "text",
          "domain": "i.redd.it",
          "allow_live_comments": true,
          "selftext_html": null,
          "likes": null,
          "suggested_sort": null,
          "banned_at_utc": null,
          "url_overridden_by_dest": "https://i.redd.it/hjv8r84ttia91.jpg",
          "view_count": null,
          "archived": false,
          "no_follow": false,
          "is_crosspostable": true,
          "pinned": false,
          "over_18": false,
          "all_awardings": [],
          "awarders": [],
          "media_only": false,
          "can_gild": true,
          "spoiler": false,
          "locked": false,
          "author_flair_text": null,
          "treatment_tags": [],
          "visited": false,
          "removed_by": null,
          "num_reports": null,
          "distinguished": null,
          "subreddit_id": "t5_2reca",
          "author_is_blocked": false,
          "mod_reason_by": null,
          "removal_reason": null,
          "link_flair_background_color": "",
          "id": "vuyxes",
          "is_robot_indexable": true,
          "report_reasons": null,
          "author": "m_ax__",
          "discussion_type": null,
          "num_comments": 184,
          "send_replies": true,
          "whitelist_status": "all_ads",
          "contest_mode": false,
          "mod_reports": [],
          "author_patreon_flair": false,
          "author_flair_text_color": null,
          "permalink": "/r/node/comments/vuyxes/deno_in_2022/",
          "parent_whitelist_status": "all_ads",
          "stickied": false,
          "url": "https://i.redd.it/hjv8r84ttia91.jpg",
          "subreddit_subscribers": 215851,
          "created_utc": 1657363081,
          "num_crossposts": 0,
          "media": null,
          "is_video": false
        }
      }
    ],
    "before": null
  }
}

格式化Zapier动作的JavaScript响应

无论你如何获得和格式化Reddit的JSON数据,我们都需要为下一步Slack机器人的整合做准备。你可以在之前的JavaScript Zapier Action中整体完成,或者将其作为自己的专用Zapier Action运行--这样,如果Reddit的格式方法发生变化,你就可以重新使用它。

这段代码相当简单,告诉你如何使用inputData 变量接受输入到JavaScript Zapier Action中,并将其作为输出。

js
const slackMessage = inputData.message
output = [{id: 123, message: slackMessage}];

严格来说,数组表示法并不是必需的,但它是一种让Zapier知道它需要重复这个动作之后的后续动作的方式,多次重复数组中的每个有效载荷(或对象值)。

下面是一个截图供参考。

JavaScript in Code Zapier Action showing how to accept input from previous actions, process them, and create a new output response for the next Zapier Action which in our case is a Slack bot integration.

在Slack中发送频道信息

我们终于到了建立Slack机器人Reddit自动化的最后一步--将我们在前几步中处理过的信息发布到Slack频道。

要做到这一点,请确保你已经将Zapier认证为Slack中的一般集成应用,并赋予它列出Slack频道的权限和其他权限,如消息发布。

添加一个新的Zapier行动,并选择Slack作为应用程序的选项。在事件表格领域,选择发送频道信息。然后,进入选择账户 选项,最好是输入你的Slack账户,这样消息就会来自你的用户资料。否则,你可以使用Slack盒子集成账户作为普通用户。

目前,这应该看起来如下。

Setup the Slack bot integration to Send Channel Message in Slack for the Reddit top posts.

然后,进入设置行动,输入以下信息。

  • 要发布消息的频道(在用户界面中表示为频道选项)
  • 消息文本选项,应该由之前的Zapier动作Message 字段预先填入。

设置应该与下面类似。

Zapier Set up Action integration for Slack bot.

最后,你可以触发这个Zapier工作流程的手动测试执行,它应该如下所示。

Zapier reset & review action.

在审查和运行Zapier工作流程的测试后,点击发布Zap按钮--这将启用工作流程,并根据我们设置为触发器的经常性时间表开始自动化。

如何将Slack API连接到其他API?

你可以建立Slack机器人API,而不仅仅是用Zapier发送一个频道消息。这里有一些Slack机器人的JavaScript想法,你可以建立在Zapier之上:当Google Sheet行更新时发送Slack消息,将接近的Google日历事件发送到Slack频道,在Slack中分享新的Twitter提及,甚至只是在Google日历事件期间或基于其他活动更新你的Slack状态,你可以与Zapier APIs连接。

你建立了一个Zapier Reddit集成!

今天,我们学习了如何建立一个Slack机器人,使用公开访问的JSON端点或Reddit oAuth API密钥,自动发送来自顶级趋势Reddit新闻的频道信息。

作为结束语,如果你正在构建一个由JavaScript代码驱动的Slack机器人集成,请确保你正在跟进相关的JavaScript安全实践,并了解相关的风险,这样你就不会因为安全问题而危及你的业务。