tawara's blog

雑記。個人の見解です。

占いしてくれる Twitter Bot を GASで作った

これは株式会社SUPER STUDIO Advent Calendar 2022の18日目の記事です。

adventar.org

きっかけ

ある日、占いをしている友人から連絡を受けた。

友達「占いの集客の一つとして Twitter アカウントを利用しててさー」

ボク「へー」

友達「特定のワードを含むリプライをされたら、自動返信をする機能が欲しいんだよね」

ボク「そういうサービスすでにあると思うよ」

友達「うん、知ってる。けどあれを使うと、Twitter for iPhone みたいな送信元の表示に、〇〇bot って表示される」

ボク「うん」

友達「Bot臭がしちゃうと、イケてないじゃんー」

ボク「そういうもんかー」

友達「できそう? できれば焼き肉とかご馳走しちゃうよ」

ボク「やってみるー。(はじめてのクライアントワークだー)」

ということで作ってみた

自動返信機能のコードだけ教えてあげようと思ったのだけれど、せっかくなので試しに占いアカウントを作ってみた。それがこちら。 このTwitterアカウントに、「占って」とリプライをすると、占い結果が返ってくる。

twitter.com

やっていることは、、、

  • 自分へのリプライを監視する
  • 占って、というワードが含まれていることの検知
  • スプレッドシートからランダムにテキストを選ぶ
  • リプライにリプライを返す

です。

こうやって作った

まずは、Twitter側で必要な準備をする。参考記事はたくさん見つかる。 ボクはこちらを参考にした。インターネットには何でもある。

【GAS】Google Apps ScriptでTwitter API v2.0を使ってみた【Twitter】 » CAMPANISTA

Twitter認証までできたら、早速自動返信機能を実装する。

まず、このようにスプレッドシートを用意する。なぜ、1なのかは、こちらを参照。

こちらのコードを書く。愚直に書いたのであまりきれいではない。

function auto_reply() {
  //リプライ内容を書いたスプレッドシートの情報を取得
  var spreadsheet = SpreadsheetApp.openById('[スプレッドシートのID]');
  var sheet = spreadsheet.getSheetByName('[リプライのテキストのあるスプレッドシートのシート]')

  //最後に取得したTwitterのリプライのIDをシートのA1セルから取得
  var last_id = sheet.getRange("A2").getValue();
  var result = {};
  result = getNewReply(last_id);
  
  // リプが0件の場合は強制終了
  if (result.result_count === 0) return;

  last_id = result.last_id;

  //GASの動作制限にかかった場合は強制終了
 if (result['replies'] == false) return;
    result['replies'].forEach(function(value) {
      var text = value['text'];
      text = text.replace(/ /g," ");
    
      //「占って」という単語に反応する
      if (text.includes('占って')) {
        var message = makeTextToSend();
        var replyTo = value['id']

        tweet_reply(replyTo, message)
        }
   }
 );

 sheet.getRange("A2").setValue(last_id);
 Logger.log(last_id);
}

//リプライをくれたTwitter ID・text・最後のリプライしたtweet_idを記憶
function getNewReply(last_id) {

  //リクエストオプション
  var options = {
    "method": "get",
    "headers": {
      "authorization": "Bearer [作成したTwitter bot のBearerトークン]"
    }
  }
  var url = "https://api.twitter.com/2/users/[運用するtwitter アカウントのID]/mentions?since_id=" + last_id
  var response = JSON.parse(UrlFetchApp.fetch(url, options));

  // リプライの情報を格納
  resp = {}
  resp['replies'] = response.data
  resp['last_id'] = response.meta.newest_id
  resp['result_count'] = response.meta.result_count
  return resp;
}

// リプライする
function tweet_reply(tweet_id, message){
  //トークン確認
  var service = checkOAuth(appname); // ここはTwitter 認証等で参考にした記事によるかもしれない
  //message本文
  var message = {
    //テキストメッセージ本文
    reply: {
      in_reply_to_tweet_id: tweet_id
    },
    text: message
  }
 
  //リクエストオプション
  var options = {
    "method": "post",
    "muteHttpExceptions" : true,
    'contentType': 'application/json',
    'payload': JSON.stringify(message)
  }
 
  //リクエスト実行
  var response = JSON.parse(service.fetch("https://api.twitter.com/2/tweets", options));
 
  //リクエスト結果
  console.log(response)
}

// リプライするテキストを取得
function makeTextToSend(){
  try {
  //リプライ内容を書いたスプレッドシートの情報を取得
  var spreadsheet = SpreadsheetApp.openById('[スプレッドシートのID]');
  var sheet = spreadsheet.getSheetByName('[リプライのテキストのあるスプレッドシートのシート]') 
  var maxRow = sheet.getDataRange().getLastRow(); //最終行の番号を取得。特定の列にしている方がいいかも
  var rand_res = getRrandRes(maxRow)//ランダムで行数を決める

  //B列:ツイート内容
  var tweetString1 = "B" + rand_res;
  var tweet1 = sheet.getRange(tweetString1);

  //リプライ内容をresultに入れて tweet_reply関数へ返す
  var result = tweet1.getValue()
  return result;
  
  } catch(e) {
 }
}

// 2以上最大行数以下の整数の乱数
function getRrandRes(maxRow){
  var x = 2
  var y = maxRow +1
  return Math.floor(Math.random()*(y-x)+x)
}

実装で困ったこと

Twitter API では自分のアカウントにリプライしてくれたツイートについて調べるAPIが用意されている。それになかなか気づけなかった。

https://api.twitter.com/2/users/[運用するtwitter アカウントのID]/mentions

このエンドポイントを叩くと、リプライをしてくれた全ツイートに関する情報を渡してくれる。便利だ。ただし、毎回全部の情報はいらない。すでにリプライしたツイートに関する情報は不要だ。なのでひと工夫する。

エンドポイントが返す情報の中には、最後にリプライしてくれたツイート情報があるので、そのツイートIDをスプレッドシートに記録しておく。「最後にリプ返したtweet_id」がそれだ。そして、次回エンドポイントを叩くときに、since_id にそのツイートIDを渡してあげれば、それ以降のリプライを拾ってくることができる。下記のように。

https://api.twitter.com/2/users/[運用するtwitter アカウントのID]/mentions?since_id=" + last_id

あとはこの処理を含む関数 auto_reply を GASのトリガー機能を使って1分ごとに動くようにすれば完成。このあたりもネットに記事が置いてある。

友人に報告

ボク「作ったよー」

友人「おおーすごいー」

ボク「焼き肉おごってー」

友人「これだけだとなー。実際は自動返信機能よりも、自動定期投稿機能が欲しかったんだよねー」

ボク「そっかー。できたらまた連絡するねー。(まじかー、ちゃんとヒアリングするべきだったー。クライアントワーク、ムズカシイ!)」

参考

【GAS】Google Apps ScriptでTwitter API v2.0を使ってみた【Twitter】 » CAMPANISTA https://campanista.com/gas_twitter/

プログラミング未経験でもできた!文系女子がGASでTwitterbot作ってみた - paiza開発日誌 https://paiza.hatenablog.com/entry/2020/02/14/バレンタインにチョコが届く!%3F_プログラミング未

Google Apps Script での 「oAuthConfig」のサポート終了後用の Twitter API スクリプト。「OAuth1」ライブラリ(ID:Mb2Vpd5nfD3Pz-_a-39Q4VfxhMjh3Sh48)が必要。 · GitHub https://gist.github.com/kijtra/f4cdd8775277372d42f7

【GAS】Twitterbotを作ってみました。サクッとできます! | Webird Programming.Tech http://webird-programming.tech/archives/274#toc8

Google Apps Script での 「oAuthConfig」のサポート終了後用の Twitter API スクリプト。「OAuth1」ライブラリ(ID:Mb2Vpd5nfD3Pz-_a-39Q4VfxhMjh3Sh48)が必要。 https://gist.github.com/kijtra/f4cdd8775277372d42f7

【爆速】自作のTwitterBotを簡単に作るライブラリを作った話【初心者向け】 | すずきライフ https://belltree.life/twitterbot-library/

Twitter Search API v1.1 と v2の 違いメモ - Qiita https://qiita.com/TakeshiNickOsanai/items/cf695370dde1ec21dbd6

GASでTwitterAPIを使用して、ツイートの内容を取得する https://zenn.dev/specially198/articles/54d0b957f185b8

Google Apps Script(GAS)でTwitter API v2の非公開統計情報を取得する https://zenn.dev/rikei_ocojo/articles/gas-twitter-non-public-metrics

Google Apps ScriptからTwitter API v2を使ってツイートする【GAS】 🌴 officeの杜 🥥 https://officeforest.org/wp/2021/05/22/gas_twitter_v2/

(了)