Cloud FunctionsでAPIを作る!

あなたが今日も積み上げる理由になりたい。岡村です。

今回は以下2つを紹介できればと思います。

経緯

前回、水族館を作った話をさせていただきました。
水族館の機能の一部として、Discordからメッセージを取得する処理が必要でした。

これまで通りGoogle Apps Scriptで全て実装できればよかったのですが、
Google Apps ScriptからではDiscordのAPIへのアクセスが
遮断されてしまうという壁に当たり、数日寝込みました。(エラーの詳細)

思いついた対策としては、DiscordのAPIにリクエストするAPIを作成すること。(まぎらわしい)
Google CloudのCloud Functionsで実装しました。

Cloud Functionsではサーバーレス環境でお手軽にコードをデプロイし、実行することができます。Cloud Functionsがリクエストを中継してくれることで無事、
遮断されずにメッセージを取得することができました。

前置きはここまで。それでは実際にやってみましょう!

この解説でやってみること

チャンネル内には以下のようなメッセージがあるという前提です。
「テストテストテスト」以降のメッセージを取得してみます。

Cloud Functionsの設定

関数作成

「ファンクションを作成」をクリックします。

関数の構成を設定

「関数名」はお好みで。

例として今回はDiscordのメッセージを取得したいので「getDiscordMessages」。
HTTPリクエストをトリガーに関数を実行したいので「トリガーのタイプ」は「HTTPS」を選択。
「認証」は「未認証の呼び出しを許可」を選択。

コーディング

以下にソースコードを置いたのでコピペしてみてください。

内容としましては、
ポストされた情報を元に、メッセージ最大取得件数である100件を上限に
Discordへメッセージ取得をリクエストしています。
レスポンスはそのままクライアントへ返しています。

index.js

const fetch = require('node-fetch');
const functions = require('@google-cloud/functions-framework');

const END_POINT_GET_CHANNEL_MESSAGES = 'https://discord.com/api/channels/';

/**
 * discordAPIにアクセスできないGASの代わりにdiscordからデータを取得します。
 * 
 * 取得できた場合は、jsonオブジェクトを返します。ステータスは200です。
 * 既に最新メッセージを取得しており、取得するデータがなかった場合は、空のjsonオブジェクトを返します。ステータスは300です。
 * エラーが発生した場合は、空のjsonオブジェクトを返します。ステータスは400です。
 *
 * @param {Object} request クライアントからのリクエストに関する機能を提供するオブジェクト。
 * @param {Object} response クラアントへのレスポンスに関する機能を提供する提供するオブジェクト。
 */
functions.http('getDiscordMessages', async (request, response) => {

  let returnJson = new Object();

  try {

    const channelId = request.body['channelId'];
    const lastMessageId = request.body['lastMessageId'];
    const botToken = request.body['botToken'];

    const urlGetChannelMessages = END_POINT_GET_CHANNEL_MESSAGES + channelId + '/messages?after=' + lastMessageId + '&limit=100';

    const headers = {
      "User-Agent": "DiscordBot " + (urlGetChannelMessages, 10),
      "Authorization": "Bot " + botToken
    }

    const options = { 
      "method": "GET",
      "headers": headers
    }

    let responseFromDiscord = new Object(); 

    try {
      responseFromDiscord = await fetch(urlGetChannelMessages, options); 
      returnJson = await responseFromDiscord.json();
      response.status(200);

    } catch(error) {
      throw new Error('discordへのリクエスト中にエラーが発生しました。');
    }

  } catch(error) {
      console.error('エラーが発生しました。error:');
      console.error(error.stack);
      response.status(400);
  }

  response.send(returnJson);
  return;
});

package.json

{
  "dependencies": {
    "@google-cloud/functions-framework": "^3.1.3", 
    "node-fetch": "^2.6.0"
  }
}

デプロイ

「デプロイ」をクリックし、完了まで待機します。
これでCloud Functions側の準備は完了です。

DiscordのBot作成

アプリケーション作成

「New Application」をクリックします。
アプリケーション名はお好みで。

レスポンスにメッセージ内容を含ませる設定

「Bot」タブをクリックします。
「MESSAGE CONTENT INTENT」をオンにします。
これを忘れるとレスポンスに含まれるメッセージが全て空になるので注意です。

Botにメッセージ読み取りを許可するためのURL作成

「SCOPES」は「bot」にチェックを入れます。
「BOT PERMISSIONS」は「Read Messages/View Channels」「Read Message History」に
チェックを入れます。

Botにメッセージ読み取りを許可する

「GENERATED URL」に生成されたURLにアクセスし、
メッセージを取得したいDiscordのサーバーを選択します。

「認証」をクリックします。
これによりBotがチャンネル内のメッセージを読み取る権限を得るので、準備は完了です。

動作確認

Thunder Clientを利用しCloud Functionsに作成した関数にリクエストしてみます。

Enter Url」にはCloud Functionsに作成した関数の詳細画面の
URL」に記載されているURLを入力します。

channelId」にはメッセージ取得したいDiscordチャンネルのIDを入力します。

lastMessageId」にはメッセージ取得範囲の開始位置としたいメッセージの
1つ前にあるメッセージのIDを入力します。

botToken」にはDiscord上に作成したBotのトークンを入力します。
Botタブの中で「Reset Token」をクリックすることで表示されます。
1度しか表示されないため、忘れてしまった場合はリセットが必要です。

「Send」をクリックするとレスポンスが返ります。
「content」にメッセージが入っていますね。

あとがき

いかがでしたでしょうか。

取得できましたら、あとはレスポンスをお好きなように扱っていただければ!

岡村の場合は、友達が毎日【今日の積み上げ】を送ってくるので、それを数えるなどしております。
スプレッドシートに最終取得メッセージを記録し、
新着メッセージのみを取得しに行くようなイメージです。

前回からおなじみとなってきた積み上げDiscordですが、
そろそろ発足してから9ヵ月くらいになるようです。
今日の積み上げポイント略して「きょうつみポイント」は
メンバー合計が350kpを超えました。

毎日の小さな努力も可視化されて積み重なれば、山のようですね。

山。山と聞いて思い出すのは、ヤマノススメ3期12話を見ながら泣いた日のこと。

また何か作ったら共有します!

お問い合わせ

サービスに関するご相談やご質問などこちらからお問い合わせください。

03-55107260

受付時間 10:00〜17:00