🕊️

Twitter APIを使わずIFTTTでツイートを収集してWebサイトに掲載する

2024/07/06に公開

近年のX(旧Twitter)の仕様変更により、Xとの連携機能が廃止されるようになってきた
https://support.nintendo.com/jp/information/2024/0509.html
https://zapier.com/l/twitter-integration-faq

どうやらTwitter API 有料化が背景にあるらしい
ツイートを取得するには、少なくとも月額$100(約1万6千円)のBasicプランが必要となる
個人開発者としてもお高い価格になっている

IFTTTを経由してツイートを収集する

任天堂もZapierもX連携を廃止したが、IFTTTならXと連携できる
Pro以上のプランが必要だが、月額$3.49(約560円)なら個人開発者でも優しい価格
https://ifttt.com/explore/updates-to-twitter-2023

たとえばこんな感じで、#premy タグが付いているツイートをWebサイトに掲載できる
https://premy.hata6502.com/
Image from Gyazo

以下のような経路で設定していく
X → IFTTT → Google Spreadsheet → Google Apps Script → Twitter widgets.jsで表示

X → IFTTT → Google Spreadsheet

Proプラン以上のIFTTTアカウントでツイートを収集し、Spreadsheetに書き込んでいく
Image from Gyazo

収集するツイートを検索クエリで指定する
↓は「リツイートを除外した#premy タグを含むツイート」
Image from Gyazo

Spreadsheetへの書き込みは、Add row to spreadsheet actionのデフォルト設定で
Image from Gyazo

Google Spreadsheet → Google Apps Script

こんな感じでツイートが収集されていく
Image from Gyazo

Google Apps Scriptを使って、ツイート情報を配信するAPIを作る
Image from Gyazo

直近15件のツイートIDを配信するスクリプト例
doGetにて、GETリクエストに対するレスポンスを生成する
IFTTTは2000行までしかSpreadsheetに書き込めないらしいので、随時古い行を削除していく処理もいずれ必要になるかもしれない

function doGet(e) {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const data = sheet.getRange("D:D").getValues();

  const tweetIDs = data
    .map(([tweetURL]) => tweetURL)
    .filter(tweetURL => tweetURL)
    .toReversed()
    .slice(0, 15)
    .map(tweetURL => tweetURL.split("/").at(-1));

  return ContentService.createTextOutput(JSON.stringify(tweetIDs))
    .setMimeType(ContentService.MimeType.JSON);
}

Google Apps Scriptをウェブアプリとしてデプロイする
全員がアクセスできるように設定する
Image from Gyazo
Image from Gyazo

https://script.google.com/macros/s/AKfycbx1Lec0RXfLou1Ixz3-hg6lFHoQdkTDSCFhtYIwQ9_OyWx36f3JYIxGdia9kLdx4DYe/exec のようなAPI URLが発行される

Google Apps Script → Twitter widgets.js

Google Apps Scriptで作ったAPIからツイートIDを取得し、Twitterのwidgets.jsを使って各ツイートをWebサイトに表示する

CSP

frame-src 'self' https://platform.twitter.com;
script-src 'self' https://platform.twitter.com;
style-src 'self' 'unsafe-inline';

HTML

<script
  async
  src="https://platform.twitter.com/widgets.js"
  charset="utf-8"
></script>

Reactでのコード例
ツイートごとに<div>を作って、twttr.widgets.createTweetでレンダリングする

const tweetIDsURL =
  "https://script.google.com/macros/s/AKfycbx1Lec0RXfLou1Ixz3-hg6lFHoQdkTDSCFhtYIwQ9_OyWx36f3JYIxGdia9kLdx4DYe/exec";

let tweetIDs: string[] | undefined;
const Tweets: FunctionComponent = () => {
  if (!tweetIDs) {
    throw (async () => {
      try {
        const tweetIDsResponse = await fetch(tweetIDsURL);
        if (!tweetIDsResponse.ok) {
          throw new Error(tweetIDsResponse.statusText);
        }
        tweetIDs = await tweetIDsResponse.json();
      } catch (exception) {
        console.error(exception);
        tweetIDs = [];
      }
    })();
  }

  const tweetContainerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!tweetContainerRef.current || !tweetIDs) {
      return;
    }
    const tweetContainerElement = tweetContainerRef.current;

    for (const tweetID of tweetIDs) {
      const tweetElement = document.createElement("div");
      twttr.widgets.createTweet(tweetID, tweetElement);

      tweetContainerElement.append(tweetElement);
    }
  }, []);

  return (
    <div ref={tweetContainerRef} />
  );
};

写真を油彩風のドット絵に変換するアプリ作りました
#premy タグをつけてツイートしてみてね
https://oil-pixel.hata6502.com/

Discussion