GoogleAppsScriptでtwitterの発言をtelegramに転送するBOTを作る(リストで複数人監視版)

#はじめに
前回[GoogleAppsScriptでtwitterの発言をtelegramに転送するBOTを作る \(複数人監視\) \- Qiita](https://qiita.com/k32ru/items/4bf7a45936ada7c0a0b4)を投稿した。その後プログラムを動かしていたが、TGへの投稿漏れが発生しているのに気づいた。ログを取ってないが、理由は簡単でホームのタイムラインがGAS定期実行(私の場合は5分間)の間に10ツイートがされてしまい、取得できなくなったのだと思う。ということで、今回はリストでデータを取得する。もちろんリストを見ることにしても、監視対象が増えてくると定期実行の間に大量にツイートがされると漏れが発生することはあるかもしれないが、そこは最大取得の数を変えるとリストを分けるとかで対応できるのではないかなかと思う。

#コード

```javascript

//前回取得したID_strを永続化するため
var properties = PropertiesService.getScriptProperties();

// Twitter AppのConsumer Api Key
var CONSUMER_KEY = "xxxxxxxx";
var CONSUMER_SECRET = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;

// 認証URLを取得しログに出力する
function logAuthorizeUri() {
  var twitterService = getTwitterService();
  Logger.log(twitterService.authorize());
}

// OAuth認証をよしなにしてくれるサービスクラスのインスタンスを生成・取得する
function getTwitterService() {
  return OAuth1.createService('Twitter')
      .setAccessTokenUrl('https://api.twitter.com/oauth/access_token')
      .setRequestTokenUrl('https://api.twitter.com/oauth/request_token')
      .setAuthorizationUrl('https://api.twitter.com/oauth/authenticate')
      .setConsumerKey(CONSUMER_KEY)
      .setConsumerSecret(CONSUMER_SECRET)
      // リダイレクト時に実行されるコールバック関数を指定する
      .setCallbackFunction('authCallback')
      // アクセストークンを保存するPropertyStoreを指定する
      .setPropertyStore(PropertiesService.getUserProperties());
}

// リダイレクト時に実行されるコールバック関数
function authCallback(request) {
  var twitterService = getTwitterService();
  // ここで認証成功時にアクセストークンがPropertyStoreに保存される
  var isAuthorized = twitterService.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput('Success');
  } else {
    return HtmlService.createHtmlOutput('Denied');
  }
}
//post test message
function postMessage(){
  postRequest('statuses/update.json', { status: "test form gas" });
}
//データを永続化するために使用
function loadCount() {
  var count = properties.getProperty("Count");
  if (!count) return 0;
  return count;
}

function saveCount(count) {
  properties.setProperty("Count", count);
}

function isThisNewPost(newStr,oldStr){
  const pad0 = num => ('0'.repeat(21)+num).slice(-20)
  return pad0(newStr)>pad0(oldStr)
}

function setLastTweetIDForce(){
  properties.setProperty("Count", 0);
}

function getListTimeline(maxGetPostCount,listID,sinceID){
  var twitterService = getTwitterService();
    if (twitterService.hasAccess()) {
      var url = 'https://api.twitter.com/1.1/lists/statuses.json?list_id=' + listID + '&count=' 
                  + maxGetPostCount + '&include_rts=true' + sinceID;
      var response = twitterService.fetch(url, {
      method: "get",
      muteHttpExceptions: true,
      });
      var getData = response.getContentText();
    } else {
      Logger.log(service.getLastError());
      return -1;
   } 
  return getData;
}
//タイムラインに流れてくる複数の特定のユーザIDのデータをTGに投稿する
function getMutiUserPostdata(){
  const maxGetPostCount = 30;
  //example : https://twitter.com/i/lists/1308764416154808321
  const watchListID = '1308764416154808321';
  var sinceID =''; 
  lastTweetID =  loadCount();
  if(lastTweetID == 0) 
  {
      sinceID ='';
  }else{
      sinceID = '&since_id=' + lastTweetID;
  }
  var getData = getListTimeline(maxGetPostCount,watchListID,sinceID)
   try {
         //例外が起きる処理
         var result = JSON.parse(getData);
         //次回このID以降のデータを取得するため、最新のツイートのIDを保存する。
         saveCount(result[0]["id_str"])
         Logger.log("lastID:" + result[0]["id_str"])
         //Logger.log(JSON.stringify(result, null, 2));
    }
    catch(e){
      //例外後の処理
      //何も読み込むデータがないと、「 エラーが発生。response:{"errors":[{"code":32,"message":"Could not authenticate you."}]}」と応答がくるみたい。
      Logger.log("エラーが発生。response:" + getData);
      return -1;
    }
    //取得したデータは配列の1番目が最新のデータとなっているので、逆順にして、古いデータから処理するようにする
    result.reverse();
    for(const post of result){
       var postIdstr = post["id_str"]
       var twName = post["user"]["name"]
       var twScName = post["user"]["screen_name"]
       var twText = post["text"]
       var tweetURL = 'https://twitter.com/' + twScName + '/status/' + postIdstr
       var tgPostBody =  twName  + ' ' + tweetURL + "\n" + twText;
       Logger.log(tgPostBody)
       sendMessage(tgPostBody)
       //Logger.log(JSON.stringify(result, null, 2));
      }
}
function testTwitterPost(){
  postRequest
}
// TwitterにAPIリクエストを送る
function postRequest(api_url, parameters) {
  var twitterService = getTwitterService();
  if (twitterService.hasAccess()) {
    var url = 'https://api.twitter.com/1.1/' + api_url;
    var response = twitterService.fetch(url, {
      method: "post",
      muteHttpExceptions: true,
      payload: parameters
    });
    var result = JSON.parse(response.getContentText());
    Logger.log(JSON.stringify(result, null, 2));
  } else {
    Logger.log(service.getLastError());
  }
}

// send to telegram
function testSendMessage(){
  sendMessage('test message from gas')
}

function sendMessage(postMessage) {
  // 以下2行は自分の環境に合わせて設定してください
  var chatId="xxxxxxxxxxx";
  var token="xxxxxxxx:xxxxxxxxxxxxxxxxxxxx";

  sendTelegram(chatId, postMessage, token);

}

function sendTelegram(chatId, text, token) {
  var payload = {
    'method': 'sendMessage',
    'chat_id': chatId,
    'text': text,
    'parse_mode': 'HTML'
  }
  var data = {
    'method': 'post',
    'payload': payload
  }
  UrlFetchApp.fetch('https://api.telegram.org/bot' + token + '/', data);
}
```

#事前準備
前回のを参考にしてください
#使い方
`getMutiUserPostdata()`を適度なタイミングで起動するようにする。
#変更点
##リストを見るようにした。
変更点としてはリストのAPIを叩くことにした点である。詳細は[GET lists/statuses \| Docs \| Twitter Developer](https://developer.twitter.com/en/docs/twitter-api/v1/accounts-and-users/create-manage-lists/api-reference/get-lists-statuses)を参照。

```javascript
function getListTimeline(maxGetPostCount,listID,sinceID){
  var twitterService = getTwitterService();
    if (twitterService.hasAccess()) {
      var url = 'https://api.twitter.com/1.1/lists/statuses.json?list_id=' + listID + '&count=' 
                  + maxGetPostCount + '&include_rts=true' + sinceID;
      var response = twitterService.fetch(url, {
      method: "get",
      muteHttpExceptions: true,
      });
      var getData = response.getContentText();
    } else {
      Logger.log(service.getLastError());
      return -1;
   } 
  return getData;
}
````

それにともなって不要になった箇所を多少変えた。

#おわりに
ホームのタイムラインを見て特定ユーザだけを抽出するなんで頭わるい作り方していたなと反省。