GASでTwitter APIを使ってみた

Twitterで APIキーを取得するには事前に「デベロッパー申請」が必要です(英語で申請目的などを書く必要があります💧)

  1. ベアラートークンを取得して保存します
  2. ベアラートークンを使ってエンドポイントにリクエストを送ります
目次
  1. トークンを取得
  2. リクエストを送る
  3. 指定したアカウントの固定ツイートをシートに書き出す

トークンを取得

TwitterAPIへのリクエスト用のOAuth 2.0ベアラートークンを取得します
エンドポイント:https://api.twitter.com/oauth2/token
Basic認証:HTTPヘッダでは特殊記号を使用できないのでユーザー名とパスワードをコロン「 :」で区切ってBase64エンコードした文字列(アルファベットと数字と一部の記号だけにする)を使います

function setupToken() {
  const API_KEY = encodeURIComponent('ここにAPI key')
  const SECRET_KEY = encodeURIComponent('ここにAPI secret key')  
  const bt = Utilities.base64Encode(`${API_KEY}:${SECRET_KEY}`);
  const params = {
    "method" : "POST",
    "headers" : {
      "Authorization" : "Basic " +bt,
      "Content-Type" : "application/x-www-form-urlencoded;charset=UTF-8",
      "Accept-Encoding" : "gzip"
    },
    "payload" : {
      "grant_type" : "client_credentials"
    }
  };
  const response = UrlFetchApp.fetch('https://api.twitter.com/oauth2/token', params);
  const data = JSON.parse(response.getContentText());
  console.log(data)
}

// { token_type: 'bearer',
//   access_token: 'AAAAAAAAA...' }

GASのPropertiesServiceクラスを利用してトークンを保存します(Key-Value形式のデータを保存できます)
例として:別の関数(getToken)から保存したトークンを呼び出します

function setupToken() {
 //省略
  const data = JSON.parse(response.getContentText());
 // console.log(data)
 if(data.access_token){
    PropertiesService.getScriptProperties().setProperty("TOKEN", data.access_token);
  } 
}

function getToken(){
  const token =  PropertiesService.getScriptProperties().getProperty("TOKEN");
  console.log(token);
}

リクエストを送る

検索キーワードからツイートを取得する例

function search() {
  const token =  PropertiesService.getScriptProperties().getProperty("TOKEN");
  if(token === null){
    token = setupToken();
  }  
//エンドポイント
  const url = `https://api.twitter.com/1.1/search/tweets.json?q=${検索ワード}&count=1`;
  const params = {
    "method" : "GET",
    "headers" : {
      "Authorization" : "Bearer " +token  ,
      "Content-Type" : "application/x-www-form-urlencoded;charset=UTF-8",
      "Accept-Encoding" : "gzip"
    },
   // "followRedirects" : true,
   // "muteHttpExceptions" : true
  };
  const response = UrlFetchApp.fetch(url, params);
  const data = JSON.parse(response.getContentText());
  const tweet = data.statuses;
  console.log(tweet);  
}

備考

  • Content-Type:”application/x-www-form-urlencoded;charset=UTF-8″
    キーと値が ‘=’ で挟まれ ‘&’ で区切られてエンコードされる
  • ContentType :”application/json”
    JSON形式で{“a”:1,”b”:2}で送る:JSON.stringify(params)

Accept-Encodingはサポートしている圧縮方式を伝えてその方式で返却してもらう

  • muteHttpExceptions:true
    404などエラーの場合でもレスポンスを返します
  • validateHttpsCertificates:true
    true : SSL証明書エラーが発生した場合でも処理を継続します。
  • followRedirects:true
    参照先にてリダイレクトが発生した場合、リダイレクト先を参照します

指定したアカウントの固定ツイートをシートに書き出す

複数のアカウントの固定ツイートを取得して、シートに書き出す
*IDはhttps://tweeterid.com/で検索
*パラメータにexpansions=pinned_tweet_idを追加すると固定ツイートがあれば取得できます
https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users#requests

function createData() {
  const data =[]
  const token =  PropertiesService.getScriptProperties().getProperty("TOKEN");
  if(token === null){
    token = setupToken();
  }  
//エンドポイント
  const url = 'https://api.twitter.com/2/users?ids=1つ目のID,2つ目のID,3つ目のID,...&expansions=pinned_tweet_id';
  const params = {
    "method" : "GET",
    "headers" : {
      "Authorization" : "Bearer " +token,
      "Content-Type" : "application/x-www-form-urlencoded;charset=UTF-8",
      "Accept-Encoding" : "gzip"
    },
  };
  const response = UrlFetchApp.fetch(url, params);
  const res = JSON.parse(response.getContentText());
  //console.log(res)
  const contents = res.includes.tweets

 contents.forEach((content)=>{
//必要な場合 HTMLタグの整形
    let text = content.text.replace(/\n/g, '<br>')
    text = AutoLink(text)
    data.push([text])
   })
  //シートに書き込む
  const ss = SpreadsheetApp.openById('シートのID');
  const sheet = ss.getSheetByName('data');
  let row = data.length;
  let col = data[0].length;
  let range = sheet.getRange(1, 1, row, col);
  range.setValues(data);
}

//固定ツイート本文にURLがあればリンクにするため
function AutoLink(str) {
    const regexp_url = /((h?)(ttps?:\/\/[a-zA-Z0-9.\-_@:/~?%&;=+#',()*!]+))/g; // ']))/;
    const regexp_makeLink = function(all, url, h, href) {
	  return '<a href="h' + href + '">' + url + '</a>';
    }
    return str.replace(regexp_url, regexp_makeLink);
}

備考(便利な関数)

文字列にURLが含まれていた場合にアンカータグを追加する

function AutoLink(str) {
    var regexp_url = /((h?)(ttps?:\/\/[a-zA-Z0-9.\-_@:/~?%&;=+#',()*!]+))/g; // ']))/;
    var regexp_makeLink = function(all, url, h, href) {
	  return '<a href="h' + href + '">' + url + '</a>';
    }
    return str.replace(regexp_url, regexp_makeLink);
}

固定ツイートのスプレッドシートへの書き込みをトリガーで定期実行
スプレッドシートから取得したデータをJSONで公開

function doGet(e){
  const data = {
    status : 'success',
    data : myData() 
  };
  const output = JSON.stringify(data);
  return ContentService.createTextOutput(output).setMimeType(ContentService.MimeType.JSON);
}

//スプレッドシートからデータを取得
function myData() {
  const ss = SpreadsheetApp.openById('シートID');
  const sheet = ss.getSheetByName('data');
  const rows = sheet.getDataRange().getValues();
  const data = rows.map(row=>{
       return {content: row[0]}   
  })
  return data;
}

公開したJSONを取得してDOMに表示する

<div id="news"></div>
async function load() {
  let error = ''
  let results = []
    try {
      let data = await fetch('エンドポイント')
      if(!data.ok || data.status == 404) {
        throw Error('取得に失敗しました')
      }
       const result  = await data.json()
       results = result.data
    }
    catch(err) {
        error =`エラー:${err.message}`
    }
    return {error, data: results}
}
(async () => {
    const el = document.querySelector('#news')
    el.innerHTML = `<div>Loading...</div>`
    const { data, error } = await load()
    let contents =  data.reduce((accu, str) => {
      return accu + `<p>${str.content}</p>`      
    }, '')
    if(error === ''){
        el.innerHTML = contents
    }
    if(error !== ''){
      el.innerHTML = `<p>${error}</p>`
    }
})()