Skip to main content

ZapierとJavaScriptでSlackボットを作成し、Redditのトレンドトピックを取得する方法

著者
blog hero webinar

2022年8月30日

0 分で読めます

Web開発の最新情報を追うのに、Redditはうってつけです。私のように、r/nodeやr/javascriptなどのサブレディットをフォローしている方も多いでしょう。最近、JavaScriptの知識だけでZapierとRedditを連携させ、話題のReddit投稿をチームのチャンネルで共有する方法を見つけました。

この記事では、次の内容を解説します。

  • Slackボットのインテグレーションの作成方法

  • ZapierでSlackチャンネルにメッセージを投稿するトリガーとアクションのワークフローを作成する方法

  • Reddit APIキーを使い、ネイティブのRedditアクセストークンとリフレッシュトークンで投稿にアクセスする方法

  • 匿名のJSONエンドポイントを使ってRedditの投稿にアクセスする方法

  • HTTPクライアントnode-fetchを使ってRedditの情報を取得するJavaScriptコードの作成方法(Node.jsバージョン10に対応。なお、すでにサポート終了しています)

Slackボットでできること

このチュートリアルではJavaScriptでSlackボットを作成する方法を紹介しますが、Zapierの機能豊富なAPIエコシステムを使えば、さまざまな情報ソースをSlackボットAPIに接続できます。このチュートリアルではJavaScriptを取り上げますが、Node.jsアプリケーションをクラウドでホストしている場合は、ZapierのHTTP Webhookを使ってSlackボットのNode.jsアプリケーションを作成することもできます。

ZapierとRedditを連携させ、注目のトピックを処理する

Zapierとは?

Zapierは、多数のプラットフォームを連携させ、ワークフローを構築できる自動化プラットフォームです。主に、Zapierマーケットプレイスで利用できるAPIやネイティブインテグレーションに使われますが、インテグレーションの各ポイントで生の入出力データを提供し、コードでワークフローの処理をカスタマイズできる点も大きな特長です。

SlackボットのインテグレーションにはZapierを使います。ZapierにはSlackとのネイティブインテグレーションがあるため、専用サーバーでボットをホストしたり、別のSlackボットインテグレーションを作成したりする必要はありません。

ZapierとSlackボットを連携させたら、次の手順に進みます。

  1. インテグレーションの実行スケジュールを設定します。Zapierのタスクを定期的に起動して処理を開始するスケジュールを指定できます。

  2. ZapierでカスタムJavaScriptコードのタスクを定義し、RedditのJSONエンドポイントから関心のあるトレンドトピックを取得します。

  3. トレンドトピックを好みの形式に整え、Zapierに組み込まれているSlackボットのネイティブインテグレーションを通じてSlackチャンネルに送信します。

ZapierのワークフローUIでは、この一連の処理は次のようになります。

週次スケジュールのトリガー、2つのJavaScriptアクション、Slackチャンネルへのメッセージ送信ステップを示すZapierワークフロー

トリガー:Schedule by Zapierで毎週実行

まず、Zapierワークフローの定期スケジュールを設定します。今回は、毎週月曜日の午前8時(UTC)に実行するように設定しました。

毎週月曜日の午前8時に実行するよう設定されたZapierのScheduleトリガー。「Refresh fields」と「Continue」ボタンが表示されています。

Slackメッセージを自動化するには?

このSlackボットのチュートリアルでは、Zapierで時間を指定して起動するスケジュールを使います。ただし、Zapier APIプラットフォームには、Slackの自動化に利用できるトリガーがほかにも数多くあります。たとえば、Twitterでのメンション、Googleスプレッドシートの更新、Googleカレンダーの予定の更新、RSSフィードの更新などをSlackボットのJavaScript自動化に組み込めます。チームの脆弱性管理のために、Snykのセキュリティ通知を自動化することもできます。

アクション:Code by ZapierでJavaScriptを実行

時間ベースのトリガーを設定してワークフローを起動できるようになったので、次はタスクを実行するアクションを作成します。Zapierのインテグレーションでは、各タスクで処理したデータを次のアクションに渡すことができます。

アクションの種類としてRun JavaScript in Code by Zapierを選びます。Eventの設定でRun JavaScriptを選択します。初期設定は次のようになります。

Zapierのワークフロー設定画面。「Code by Zapier」で「Run Javascript」イベントが選択され、「Continue」ボタンと完了済みのアクションセクションが表示されています。

次に、Set up actionの設定を展開し、Reddit APIエンドポイントからトレンドトピックの情報を取得するJavaScriptコードを追加します。

Reddit APIキーの使い方

Redditの最新情報を取得する方法はいくつかあります。ここでは、興味のあるRedditスレッドの更新情報を受け取る方法を2つ紹介します。

  1. Reddit APIキーを使う方法(Redditユーザーアカウントの登録が必要)

  2. 一般公開されているReddit APIエンドポイントを使う方法

サブレディットやトレンド投稿の読み取りだけが必要な場合は、Reddit OAuth APIのワークフローを使わないシンプルな方法がおすすめです。Reddit APIへのアクセスに関するこの章の2つ目の方法に進んでください。

Reddit APIキーを登録する

まず、Reddit APIアプリケーション設定に移動し、画面下部のCreate an appボタンをクリックします。次に、APIのユースケースに関する情報を入力します。アプリケーションの種類をwebappに設定し、アクセストークンを処理するためのリダイレクトURIを指定してください。

名前、説明、About URL、リダイレクトURIの入力欄と、Webアプリ、インストール型アプリ、スクリプトの選択肢があるアプリケーション作成フォーム。

するとClientIDとClientSecretが発行され、これらを使ってAPIを呼び出せるようになります。Reddit APIへの問い合わせ方法やReddit OAuthの手順がわからない場合は、Redditのスクレイピングに関する記事など、ほかの入門チュートリアルを参照してください。

Reddit APIのルールと遵守すべき条件を理解するため、必ずRedditのAPIアクセス規約を確認してください。

Reddit APIを使ったJavaScriptの例

ここでは、JavaScriptを使って興味のあるRedditスレッドを取得する方法を見ていきます。そのために、上記のClientIDとClientSecretの認証情報に加えて、refresh_tokenの値が必要です。この値は、Reddit OAuthの永続アクセストークンを作成する設定を選んだ場合に取得できます。

以下は、ネイティブのJavaScriptコードでサブレディットの投稿を取得するReddit APIキーの使用例です。コードスニペットを正しく動作させるには、以下のABCDを先ほどのReddit認証情報に置き換えてください。

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サブレディットに投稿されたトレンド投稿を取得します。最後に、すべての投稿データをループ処理し、次のアクションに渡すメッセージ文字列を作成します。

先ほどのコードスニペットから、特に重要な点を2つ紹介します。

  1. Redditのデータを問い合わせる正しい最新のAPIでは、params2で使われているtクエリパラメーターを使用します。ただし、古いチュートリアルやガイドでは、今もtimeを使うように書かれている場合があります。

  2. もう一つ注目すべき点として、Zapierではサポートが終了したNode.jsランタイム(Node.js v10)上で古いバージョンのnode-fetchを使う必要があります。そのため、URLオブジェクトを直接使ってHTTPリクエストを実行できます。

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

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

ほかのAPI操作については、Redditアカウント、履歴、サブレディットのモデレーションなどに関する利用可能なエンドポイントを網羅した、最新のReddit APIドキュメントを参照してください。

公開JSONエンドポイントを使ってRedditの情報を取得する

次のJavaScript Zapierアクションに進む前に、OAuthやほかのAPIキーによる認証を行わずにReddit APIに問い合わせる方法を簡単に紹介します。

Redditでは、サブレディットのスレッド情報をJSON形式で、次の公開認証不要エンドポイントから取得できます。

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

このサブレディットのTop投稿を取得し、次のクエリパラメーターを追加して期間を指定することもできます。

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アクション内でまとめて処理することも、専用のZapierアクションとして実行することもできます。後者なら、Redditのデータ形式が変わった場合にも再利用できます。

次のコードスニペットはシンプルです。inputData変数を使ってJavaScript Zapierアクションに入力を渡し、出力として返す方法を示しています。

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

配列形式は必須ではありませんが、これを使うと、配列内の各ペイロード(またはオブジェクトの値)に対して、後続のアクションを繰り返し実行するようZapierに指示できます。

参考までに、スクリーンショットを以下に示します。

Zapierの「Run Javascript in Code」アクションに、マッピングされたメッセージ入力と、Slackメッセージをログに記録して出力オブジェクトを返すJavaScriptコードが表示されている。

Slackでチャンネルメッセージを送信する

いよいよ、SlackボットによるReddit自動化の最後のステップです。前の手順で処理したメッセージをSlackチャンネルに投稿します。

そのために、Zapierを汎用のインテグレーションアプリケーションとしてSlackに認証し、Slackチャンネルの一覧表示やメッセージの投稿などに必要な権限を付与してください。

新しいZapierアクションを追加し、ApplicationにSlackを選びます。Eventの入力欄ではSend Channel Messageを選択します。次に、Choose Accountオプションに進み、メッセージが自分のユーザープロフィールから送信されるよう、できれば自分のSlackアカウントを選びます。そうでない場合は、汎用ユーザーとしてSlackボックスのインテグレーションアカウントを使用できます。

この時点で、次のようになっているはずです。

JavaScriptアクションの後にSlackアクションを配置し、チャンネルへのメッセージ送信を設定したZapierのワークフロー

次にSet up actionに進み、以下の情報を入力します。

  • メッセージの投稿先チャンネル(UIではChannelオプションとして表示)

  • Message Textオプション。前のZapierアクションのMessageフィールドがあらかじめ入力されているはずです。

設定は次のようになります。

Slackチャンネルへのメッセージ送信に使うフィールド(メッセージ本文、ボット名、画像URL、リンクオプション)が表示されたZapierの設定画面。

最後に、このZapierワークフローを手動でテスト実行できます。実行結果は次のようになります。

Node.jsのReddit投稿リンクを含むSlackメッセージデータ、Zapの「再テストと確認」ボタンと「Zapを公開」ボタンが表示されたZapierの確認画面

Zapierワークフローを確認してテストを実行したら、Publish Zapボタンをクリックします。これでワークフローが有効になり、トリガーとして設定した定期スケジュールに基づく自動化が開始されます。

Slack APIをほかのAPIに接続する方法

Zapierを使えば、チャンネルメッセージの送信以外にもSlackボットAPIを構築できます。Zapierを活用したSlackボットのJavaScriptアイデアをいくつか紹介します。Googleスプレッドシートの行が更新されたときにSlackメッセージを送る、Googleカレンダーの予定が近づいたらSlackチャンネルに通知する、新しいTwitterのメンションをSlackで共有する、Googleカレンダーの予定やほかのアクティビティに合わせてSlackのステータスを更新する、といった自動化が可能です。

ZapierとRedditのインテグレーションが完成しました!

今回は、一般公開されているJSONエンドポイントまたはReddit OAuth APIキーを使って、Redditの注目ニュースをチャンネルメッセージとして自動送信するSlackボットの作成方法を紹介しました。

最後に、JavaScriptコードを使ったSlackボットのインテグレーションを構築する場合は、関連するJavaScriptセキュリティのベストプラクティスに従い、リスクを理解したうえで、セキュリティ上の問題によってビジネスを危険にさらさないようにしてください。

カテゴリー: