5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

にゃーんツイートを感知してDiscordに猫を派遣するbotを作る

Last updated at Posted at 2020-02-22

ふざけたタイトルですが下記の事をそれなりに丁寧にやります。

  • IFTTT でツイートを取得、情報をスプレッドシートに書き込み
  • GoogleAppsScript をTypeScriptで開発(+ 環境整備)
  • DiscordのWebHookを利用し、botにメッセージを送らせる

ってことでやっていこう。
全文はかなり長いので、適宜必要な情報だけ拾ってください。

目次

  1. GASの準備
  1. IFTTTとやり取りをするための準備
  2. IFTTTを設定
  1. DiscordのBot準備
  1. GASでDiscordへメッセージを送る
  1. IFTTTとDiscordをつなげる
  1. 鳴く!

使ったもの

  • node.js 12.16.0
  • Visual Sutudio Code 1.42.1

VSCodeとnode.jsはインストール済みのものとして進めていきます。丁寧とは
上記に関しては調べれば簡単にインストールができるので、ない場合はそうしてください。

具体的に何をするの?

初めに大体書きましたが、具体的には

IFTTTでTwitterのツイートを取得し、スプレッドシートに書き込み、
それをGASが検知して、ツイート内容を解析。
ツイート内容が "にゃーん" だったらDiscordへ猫の絵文字を流させる

という流れです。
ざっくり言うと、Twitterで社会性フィルターを感知するとDiscordに猫が駆けつけてくれるbotを作ります

GASの準備

GASってなんやねん

GoogleAppsScriptの略です。Googleアカウントさえあれば無料で使えます。
Googleのサーバー上で動作するスクリプトで、スプレッドシートやGmailなどGoogleの各種サービスを叩けます。
詳しくは調べればたくさん出てくるので、興味があればぜひ。

今回は無料で建てられるサーバーとしてIFTTTとDiscordの仲介役になってもらいます。

やっていき

GASを動かすためのいろいろを準備します。

セットアップ

今回はTypeScriptとClasp(GASをVSCodeで使うためのパッケージ)を用いて開発するので、clasp が Typescript をサポートした!を参考に必要なパッケージをインストールしていきます。
VSCode上でctrl+@するとターミナルが開くので、そちらにコマンドを入れていきましょう。

cmd
$ mkdir nyan-bot
$ cd nyan-bot
$ npm init -y
$ npm install @google/clasp -D 
$ npm install @types/google-apps-script -S

Typescriptはこちらでインストールしなくても@google/claspが依存しているので勝手にインストールされます。
トランスパイルはpushの際にclaspが勝手にやってくれるので、特にTypeScriptの諸々を設定する必要はありません。

ログイン

とりあえずgoogleアカウントへログインを済ませましょう
以下を実行するとブラウザが開き、必要情報を入力するとログインできます。

cmd
$ clasp login

GASを有効化

GoogleのフォームでGASを有効化しておかないと一部コマンドが使えないので、GASを過去に一回も使ったことがない人は
https://script.google.com/home/usersettings
にアクセスしてGoogleAppsScriptAPIをオンにしてきてください。
bandicam 2020-02-18 01-37-37-239.jpg

スクリプトとスプレッドシートを作成

ログインを済ませたらGASのスクリプトを作成します。
今回はスプレッドシートを使うので、以下のコマンドを実行したら
"Create which script?" にはsheetsと答えましょう

cmd
$ clasp create

bandicam 2020-02-18 00-59-43-985.jpg

ここまで正しく工程を踏めていれば以下のようにスクリプトとスプレッドシートのリンクが吐き出されます
bandicam 2020-02-18 01-00-43-439.jpg

扱いやすいようにフォルダと設定を整理

.clasp.jsonにrootDirを設定することで、rootDirで指定したディレクトリ以下のみがGASへpushされるようになります。

.clasp.json
{ 
    "scriptId":"xxxxxxxxxxxxxxxxxx-xxxx",
    "rootDir": "./src"
}

以下の様なフォルダ構成にします。
pushの際、 appsscript.json を含めないといけないため、appsscript.json は必ずrootDirで指定したフォルダ以下に移動しておいてください。

nyan-bot/
├── .clasp.json
├── node_modules/
├── package-lock.json
├── package.json
|
├── node_modules
└── src
    └─ ts
    └─ appsscript.json

ひとまずGASのセットアップは終わりです。お疲れ様。

GASのテスト

セットアップが終わったので、まずは動作のチェックをします

.tsファイルを作る

以下のmain.ts ファイルを src/ts 下に作ります

main.ts
function TestAction()
{
    //アクティブなスプレッドシートを取得する
    const spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
    //一番初めのシートを取得する
    const firstSheet = spreadSheet.getSheets()[0];
    //セルに書き込む
    firstSheet.appendRow(["こんにちは世界","Hello World","なんでプログラマすぐあいさつするん?"]);
};

GASへpush

以下のコマンドでpushできます

cmd
$ clasp push

bandicam 2020-02-19 23-07-57-263.jpg

pushしたらGASへ確認しに行きましょう。
URLで飛んでもいいですが、下記のコマンドでもアクセスできます。

cmd
$ clasp open

pushがうまくいっていればトランスパイルされた main.gs と再会できます
.gsとありますが、言語は JavaCusoript JavaScriptです
bandicam 2020-02-19 23-14-45-660.jpg

実行

GASは関数単位で実行されます。
電球マークの隣が TestAction になっていると思いますが、ここのリストに実行可能な関数名が列挙されます。
今回実行するのはそのまま TestAction なので、その左にある三角マークを押して実行しましょう。

関数内にスプレッドシートをいじる処理があるので、初めて実行する時は許可を求められます。
とりあえずはいはい言って許可を出します。

実行後、スプレッドシートに以下の様に書き込まれていれば成功です。
bandicam 2020-02-19 23-23-22-431.jpg

GASの準備は一通り完了。次はIFTTTに移ります。

IFTTTとやり取りをするための準備

といいましたが、先にスプレッドシート側の受け入れ準備をします。

シートを整理

IFTTTからツイート内容を受け取るシートRecvTweetと、
GASのログを出力するシートLogをそれぞれ用意しておきます。
bandicam 2020-02-22 15-53-46-102.jpg

スプレッドシートのパスを変更

また、スプレッドシートは現状Googleドライブのルートディレクトリに直置きされているので、IFTTTフォルダを作ってその中に放り込んでおきます。
(IFTTTでスプレッドシートのパスを選択する際、ルートディレクトリが選べないので)
bandicam 2020-02-22 17-08-51-100.jpg

これで準備は完了。IFTTTへ移りましょう

IFTTTを設定

IFTTTってなんやねん

イフトと読みます。IF This Then That(もし、これをしたらあれをする)の略です。
この子はInstagram、Twitter、Googleドライブなどのプラットフォームの仲介をしてくれます。
TwitterでツイートしたらGoogleドライブのスプレッドシートに書き込む、みたいなことができます。便利ですね。

ということでそれを利用します。

やっていき2

IFTTTの設定はこの記事を参考にしました

こちらを参考に、ツイートを感知してスプレッドシートへ書き込む機構を作ります。

まずはIFTTT にアクセスしてログインしましょう
Googleアカウントでログインできるので、アカウントがなくても簡単に入れます。

レシピを作成

右上の自分のアイコンをクリックし、Createを選びます
bandicam 2020-02-20 00-14-17-512.jpg

Twitter側の設定

Create後、以下の様な画面になるので、If This Then That の発火条件に当たる [+]This をクリックしましょう。
bandicam 2020-02-20 00-15-27-203.jpg

クリックすると大量のプラットフォームが出てくるので、その中からTwitterを選びます。
マウスホイールを虐めながらTwitterを探すのもいいですが、Search servicesを利用した方がマウスホイールにやさしいです。

Twitterを選んだら、次はトリガー(発火条件)を選びます。
今回は自分がツイートしたときに発火して欲しいので、左上の New tweets by you を選択し、
Create Trrigerしましょう。
bandicam 2020-02-20 00-19-26-043.jpg

bandicam 2020-02-20 00-23-31-711.jpg

これで This、発火条件は設定完了です。

スプレッドシート側の設定

続いて [+]That をクリックしてアクションを設定しましょう。
さっきのTwitterと同じようにして、今度はGoogleSheetsを選びます。
スプレッドシートの更新を見てGASを起動するので、TriggerはUpdate cell in spreadsheetにします。

するとActionの設定が出てきます。項目は以下の通り。
Drive folder path: 書き込みたいスプレッドシートのGoogleドライブ上のパス
Spreadsheet name: 書き込みたいシートの名前
Which cell?: 書き込むセル番地
Value:書き込む内容。Add ingredientを押すと、Twitterからの情報を選べるので、Text(本文)とLinkToTweet(ツイートへのリンク)を選んでおきます。
以下のように設定したらCreate action。
bandicam 2020-02-22 18-35-43-626.jpg

後はFinishをクリックで終了。

動作テスト

動作させるために、先ほど作ったレシピをConnentさせておく
下のトグルスイッチに"Connected"と表示されてたらokです
bandicam 2020-02-22 19-15-14-784.jpg

This(Twitter) の動作チェック

[+]This でTwitterを選んだ時にログインしたアカウントでツイートしてみましょう。
bandicam 2020-02-22 19-24-08-957.jpg
ではIFTTTがツイートを拾えているか確認しに行きましょう。
先ほど作ったレシピのSettingから、View activityを開きます。
ツイートの内容が拾えていれば以下の様に表示されいます。
ただ、ツイートしてから拾うまでのラグがまちまちのため、しばらく待たないと出て来ないことが多いです
bandicam 2020-02-22 19-27-06-491.jpg
ここで表示されていない場合は、記事を遡りつつ以下の可能性を考慮してみてください

  • IFTTTが拾うのに失敗している(何度かツイートしてみてください)
  • Connectしてない(IFTTTのレシピがConnectedになってるか確認してください)
  • ツイートするアカウントを間違えている

That(スプレッドシート)の動作チェック

GASのスプレッドシートを開いて確認してください。
一番目のシートのA1にツイート内容とリンクが入力されていればokです
bandicam 2020-02-22 19-38-52-383.jpg
ここで、GASのスプレッドシートではなく新しくスプレッドシートが生成されてしまっている場合はスプレッドシートの配置、もしくはIFTTTのスプレッドシートのパス設定が間違っています。

ここまでうまくいっていればIFTTTの準備は終わり。お疲れ様です

DiscordのBot準備

さて、入力側の準備が終わったので今度は出力側の準備をしましょう。
BotといってもWevhookというものを使った機構なので、よく使うサーバーに呼び込んで使うものとはちょっと違います。

Webhookってなんやねん

こちらの記事に詳しく書かれています
とりあえずは他のアプリケーションとやりとりするための機構だと思ってもらえればokです。

やっていき3

派遣する猫が迷わないようにします。
もとい、サーバーのWebhookを作成します

こちらの記事を参考にしました。

サーバーの準備

Webhookを作成するにはサーバーでの権限が必要です。
他人のサーバーで猫を受け取りたい場合は場合は管理人に相談して権限をもらってください。
もしくは自分でサーバーを立てましょう。

テキストを流すチャンネルの準備

派遣された猫を受け入れるテキストチャンネルを用意します。
bandicam 2020-02-22 20-57-21-248.jpg

Webhookの作成

サーバーの設定を開き、Webhookを作成します。
サーバーの設定にウェブフックの項目がない場合はそのサーバーでの権限がない状態なのでどうにかしてください。
bandicam 2020-02-22 21-02-43-966.jpg
bandicam 2020-02-22 21-02-54-878.jpg

Webhookの設定

Webhookを作成したら設定が開くので、お好みの情報を入力してください。
猫を受け取った時、ウェブフックのアイコンお名前 を使ったBotがチャンネルにメッセージを送ってくれます。
また、一番下のウェブフックURLはGASからDiscordにメッセージを送るのに使うため、控えておいてください。
bandicam 2020-02-22 21-10-34-959.jpg
とりあえず私はこんな感じの設定にしました
bandicam 2020-02-22 21-18-50-338.png

保存を押せばDiscordの受け入れ準備は完了です

GASでDiscordへメッセージを送る

Discord側の受け入れ準備は終わったので、GASからメッセージを送りこめるようにします

やっていき4

VSCodeで、GASの準備で作ったプロジェクトを開いてください。

以下の様なフォルダ構成でやっていきます
各ファイルについてはこれから説明していきます

nyan-bot/
├── .clasp.json
├── node_modules/
├── package-lock.json
├── package.json
|
├── node_modules
└── src
    ├─ ts
    |  ├─ discord
    |  |  └─ IDiscordSendParameters.ts
    |  |  └─ IDiscordWebhookPayload.ts
    |  |  └─ WebhookSender.ts
    |  └─ gas
    |     └─ SpreadSheetUtil.ts
    └─ main.ts
    └─ appsscript.json

ログ出力を実装

簡単にスプレッドシートにログを出す機構を先に作っておきます

SpreadSheetUtil.ts
export default class SpreadSheetUtil 
{
    //テキストのログ書き込み
    public static WriteToLogSheet = (text: string) => {
        //スプレッドシートを取得
        const spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
        //シート`Log`を取得
        const sheet = spreadSheet.getSheetByName("Log");
        //シート`Log`の最終行にtextを書き込み
        sheet.appendRow([text]);
    };
    //HTTPResponse用のログ書き込み
    public static WriteResponseToLogSheet = (
      response: GoogleAppsScript.URL_Fetch.HTTPResponse
    ) => {
        SpreadSheetUtil.WriteToLogSheet(
        `[${response.getResponseCode()}] ${response.getContentText()}`
      );
    };
}

SpreadSheetUtil.WriteToLogSheet("ログ")
で、スプレッドシートのLogにログが出せます。
テストしてみましょう。
main.tsをいじってGASにpushします

main.ts
import SpreadSheetUtil from "./gas/SpreadSheetUtil";

function LogTest()
{
    SpreadSheetUtil.WriteToLogSheet("丸太テスト");
};

bandicam 2020-02-22 22-23-13-849.png

実行して、スプレッドシートのLogに出力されていれば成功です。
bandicam 2020-02-22 22-28-35-993.png

Discordのメッセージ型を実装

Discordにメッセージを送らせる、と言っても普通に送ってもWebhook君には理解できません。
Jsonで送る必要があります。
ということでこちらの記事を参考にメッセージInterfaceを作ります。
各ステートの詳細は参考記事の方を見てください。

IDiscordWebhookPayload.ts

export default interface IDiscordWebhookPayload {
    username?: string;
    avatar_url?: string; 
    content?: string; 
    embeds?: [ 
      {
        title?: string;
        description?: string;
        url?: string;
        timestamp?: string;
        color?: number;
        footer?: {
          text?: string;
          icon_url?: string;
        };
        image?: {
          url?: string;
        };
        thumbnail?: {
          url?: string;
        };
        author?: {
          name?: string;
          url?: string;
          icon_url?: string;
        };
        fields?: {
          name?: string;
          value?: string;
          inline?: boolean;
        }[];
      }
    ];
  }

DiscordのWebhookにPostリクエストを送る

メッセージの型も準備ができたので、いよいよDiscordにメッセージを送る機構を作っていきます。

HttpRequestパラメータを定義

初めにHttpRequestのパラメータ型を定義しておきます

IDiscordSendParameters.ts
export default interface IDiscordSendParameters
{
    method: "get" | "delete" | "patch" | "post" | "put";
    payload: string;
    contentType: string,
    muteHttpExceptions: boolean;
}

DiscordにPostリクエストを送る機構を実装

先ほどのパラメータを使ってDiscordにPostリクエストを送ります。
DiscordWebHookSender.webhookURLにはDiscordのBot準備で取得したウェブフックURLを入力してください。

WebhookSender.ts
import IDiscordWebhookPayload from "./IDiscordWebhookPayload";
import IDiscordSendParameters from "./IDiscordSendParameters";
import SpreadSheetUtil from "../gas/SpreadSheetUtil";

export default class DiscordWebHookSender
{
    //DiscordのウェブフックURL
    public static webhookURL:string="https://discordapp.com/api/webhooks/xxxxxxxxxxxx/xxxxxxxxxxxxxxxxxxxx";

    //Postプロセスを送る
    public static Send(payload: IDiscordWebhookPayload):void
    {
        //Postリクエストのパラメータを作成
        const params: IDiscordSendParameters = {
            method: "post",
            payload: JSON.stringify(payload),
            contentType: 'application/json',
            muteHttpExceptions: true //エラーを起こしたときに、エラーの代わりに内容をHttpResponseを返してくれます。
        };
        
        //ログに、Discordに送るパラメータを表示
        SpreadSheetUtil.WriteToLogSheet("[Throw Discord Message]: "+params.payload);
      
        //DiscordにPostリクエストを送信
        const response = UrlFetchApp.fetch(this.webhookURL, params);
        //レスポンスをログに出力
        SpreadSheetUtil.WriteResponseToLogSheet(response);
    }
}

Discordにメッセージを送る!

さて、Discordにメッセージを送る準備が整いました。
ということで動作テストです。
main.tsをいかに書き換えてGASにpushしましょう

main.ts
import SpreadSheetUtil from "./gas/SpreadSheetUtil";
import IDiscordWebhookPayload from "./discord/IDiscordWebhookPayload";
import DiscordWebHookSender from "./discord/WebhookSender";

function DiscordTest()
{
    const payload:IDiscordWebhookPayload=
    {
        content:"にゃんにゃん"
    };

    DiscordWebHookSender.Send(payload);
};

bandicam 2020-02-22 23-30-12-797.png

実行して、Discordでメッセージが送られれば成功です。
合わせてログも確認しましょう。
うまく送信できてない場合は[204]ではなくLogにエラーメッセージが出ます。
bandicam 2020-02-22 23-29-23-598.png
bandicam 2020-02-22 23-39-06-675.png

IFTTTとDiscordをつなげる!

準備は整った!あとはスプレッドシートに書きこまれるツイートを感知して、Discordに猫を派遣するのみです

スプレッドシートに書き込まれたツイートを読み込む機能を実装

ts:SpreadSheetUtil.tsに、スプレッドシートに書き込まれたツイートを読み込む機能、ReadRecvTweetを追加します

SpreadSheetUtil.ts
export default class SpreadSheetUtil 
{
    //テキストのログ書き込み
    public static WriteToLogSheet = (text: string) => {
        //スプレッドシートを取得
        const spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
        //シート`Log`を取得
        const sheet = spreadSheet.getSheetByName("Log");
        //シート`Log`の最終行にtextを書き込み
        sheet.appendRow([text]);
    };
    //HTTPResponse用のログ書き込み
    public static WriteResponseToLogSheet = (
      response: GoogleAppsScript.URL_Fetch.HTTPResponse
    ) => {
        SpreadSheetUtil.WriteToLogSheet(
        `[${response.getResponseCode()}] ${response.getContentText()}`
      );
    };
    
    //ツイートの読み込み
    public static ReadRecvTweet():string {
        //スプレッドシートを取得
        const spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
        //シート`RecvTweet`を取得
        const sheet = spreadSheet.getSheetByName("RecvTweet");

        //A1の内容を返す
        return sheet.getRange("A1").getValue();
    };
}

"にゃーん"かどうか判別して、猫を派遣する

main.tsに、スプレッドシートが変更されたときに走る処理を書きます。
ツイートの内容がにゃーんなら猫を派遣し、それ以外が来た場合は派遣しません

main.ts
import SpreadSheetUtil from "./gas/SpreadSheetUtil";
import IDiscordWebhookPayload from "./discord/IDiscordWebhookPayload";
import DiscordWebHookSender from "./discord/WebhookSender";

function SocialFilterCat()
{
    //ツイートを取得
    const tweet=SpreadSheetUtil.ReadRecvTweet();

    //ツイートににゃーんが含まれるか解析
    if(tweet.indexOf("にゃーん")===-1)
    {
        //社会性フィルターじゃないので返る
        SpreadSheetUtil.WriteToLogSheet("<"+tweet+"> is not social filter");
        return;
    }

    //URLを取得
    const urlIndex=tweet.lastIndexOf("http://twitter.com/");
    const tweetURL=tweet.substring(urlIndex);

    const payload:IDiscordWebhookPayload=
    {
        content:":cat: \n"+tweetURL
    };

    DiscordWebHookSender.Send(payload);
};

できたらGASへpushして、SocialFilterCatを実行します。
実行したらLogを確認してください。
"にゃーん"とツイートしていなければこういうLogが出ると思います
bandicam 2020-02-23 00-13-33-493.png

続いて、スプレッドシートの内容を"にゃーん"に変えて実行してみましょう。
bandicam 2020-02-23 00-15-12-465.png
bandicam 2020-02-23 00-15-28-991.png

可愛い猫ちゃんが派遣されました。

IFTTTからの書き込みを検知して自動で実行する

これで一応猫を派遣できるようにはなりましたが、
手動でしか動かないのでまだ流しそうめんを一人でやっているような感じです。
…ということでIFTTTからのツイート書き込みを検知して自動で動くようにしましょう

トリガーを設定

発火条件、トリガーを設定します。
GASの編集->現在のプロジェクトのトリガーを開きます
bandicam 2020-02-23 00-18-12-817.png

開いたらトリガーを追加をクリック。
以下の設定にして保存します。
スプレッドシートで変更が行われたらSocialFilterCatが実行されるといった感じ。
bandicam 2020-02-23 00-20-18-889.png

鳴く!

bandicam 2020-02-23 00-26-04-360.png
bandicam 2020-02-23 00-31-17-349.png
bandicam 2020-02-23 00-30-43-108.png

…終わります。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?