Astro、microCMSで作成しているブログ新規投稿を、GASで無料で自動ツイートするようにしてみた。

## ブログの新規投稿を、自動でTwitterに投稿してほしい！
microCMS＋Astroを利用した"令和最新版のブログ"は、カテゴリーページや、google Adsenseの広告追加など、いわゆる世間一般で考えられるようなブログの機能を少しずつ実装してきました。
コンテンツである内容は、まぁ、あんまりないんだけど、機能的に大手ブログサービスにもありそうな、ブログらしいブログになってきたんじゃないかな？と思っています。
ちなみにこちら↓です。よかったらみてください。内容の無さと広告の必死さに感動するかもしれません。

https://blog.calocenrieti.com/

ただ、ブログ開設当初からやりたいと思いながら実現できていなかったのが、ブログ新規投稿時のTwitter連携。
よくあるブログサービスでは、新規投稿時にTwitterにも投稿する、というような機能があったりしますが、これが実現できていませんでした。
とりあえずブログにTwitter投稿ボタンを配置して手動でTwitter投稿していたのですが、正直めんどくさい！

結論的には、表題の通り、GASを利用して実現したのですが、やろうとして断念したことも書いておきます。

## microCMSのwebhookを利用してTwitter投稿できないか
microCMSにはwebhookの機能があるので、これをgithubアクションのトリガーとして、Astroのビルドをしています。
同じように、新規投稿のトリガーでwebhookが飛んで、Twitter連携できないか調べました。

microCMS公式には、次の方法が記載されています。

https://blog.microcms.io/custom-notification-twitter

[Zapier](https://zapier.com/)というサービスを利用して、webhook → Zapier → Twitterと連携する形のようです。
フリープランがもあるので、よしよし、と思って準備を進めていたのですが、Twitter（自称X）への連携はフリープランでは利用できませんでした。
趣味で構築しているブログで、お金をかけるほどの価値は現状ないので、Zapierの利用は断念しました。

他に何かないのか？と検索して見つけたのが、[IFTTT](https://ifttt.com/)でした。
こちらもフリープランがあり、先ほどのZapierと同様に、webhook → IFTTT → Twitterと連携する形のようで、準備を進めていったところ、こちらもTwitter（自称X）への連携はフリープランでは利用できず、Proプランでなければ利用できない模様。

恐らく自称XくんがAPIの無償利用を排除し高額なAPI使用料を請求するようになった結果、Twitterへの連携サービスは基本的に有償になったのだろうと思います。

## Twitter API v2、OAuth 2.0、GASで実装
参考にした記事はこちらです。

https://zenn.dev/nyancat/articles/20230803-twitter-api-oauth2-pkce

https://qiita.com/yuzinet/items/ae4b9ca2b5cd989de435

前者の方で、CLIENT_ID、CLIENT_SECRETなどの取得まで実施し、後者を参考にしてGASでのTwitter投稿を実装しました。後者はほぼそのまま使わせていただいています。ありがたやー。
microCMSのwebhookをアテにしていたのですが、現状webhook → githubアクションでのAstroのビルドには2~3分を要しています。仮にwebhookを利用してTwitter投稿すると、ビルド中でまだ存在しないウェブページへのリンクを投稿することになってしまい、あまりよろしくありません。
かといって、適当なwaitを入れるにしても、いつAstroのビルドが終わっているのかよくわかりません。
（ビルドに時間がかかり過ぎでそのうちなんとかしたいとは思っているのですが、そこまで困ってないので優先度が低めなのです）

そこでwebhookを諦めて、ウェブスクレイピングの形での実装を考えました。
ウェブスクレイピングであれば、作成されたウェブページだけが抽出されるはずで、webhookよりも安心感はあります。（但し定期的にアクセスするため、ウェブサイト側が過負荷にならないように注意が必要ですが）
幸いAstroではRSSフィードのXMLを作成できる仕組みがあり既に実装できていたので、こちらを読み込んで、新規投稿があれば、Twitter投稿する形としました。

GASのスクリプトは、参考にさせていただいた後者のtwitterOAuthスクリプトほぼそのまま使わせていただいて、RSS（XML）の分析、差分確認と変更データのスプレッドシートに保存部分を新規に記述しました。
一度に複数投稿することはまずないので、差分は最初の1件だけ取得しURLが前回と異なるかだけ確認しています。
RSSの差分確認とTwitter投稿は、関数``rss_change_twpost``を時間トリガーで15分おきに関数を実行することで実現しています。

## スクリプトと投稿例

<details><summary>google apps script</summary>

RSSを取得してタイトルとURLを返す関数``parseXml``

```ruby:parseXml
function parseXml() {
  let url = 'https://***************'; // RSSのアドレス
  let xml = UrlFetchApp.fetch(url).getContentText();
  let document = XmlService.parse(xml);
  let root = document.getRootElement();

  let channel = root.getChild('channel');
  let items = channel.getChildren('item');
  
  // for (var i = 0; i < items.length; i++){
  for (var i = 0; i < 1; i++){
  // items.forEach(item => {
    var title = items[i].getChild('title').getText();
    var link = items[i].getChild('link').getText();
    // console.log('%s ,%s', title, link);
  };
  var rss = {
    TITLE: title,
    URL: link,
  };
  return rss;
}
```

RSS取得とスプレッドシートの値を比較してURLが異なればTwitter投稿するメイン関数``rss_change_twpost``
こちらの関数を15分おきに時間トリガーで実行しています。

```ruby:rss_change_twpost
function rss_change_twpost(){
  let rss =parseXml();
  // console.log(rss.TITLE,rss.URL);
  let ss =getSpreadsheet_value();
  // console.log(ss.TITLE,ss.URL);
  if (rss.URL != ss.URL) {
    // console.log("違う");
    setSpreadsheet_value(rss.TITLE,rss.URL);
    postTwitter(rss.TITLE,rss.URL)

  }
}
```

スプレッドシートにタイトルとURLを書き込む関数``setSpreadsheet_value``

```ruby:setSpreadsheet_value
function setSpreadsheet_value(title,url) {
  const ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/**********/edit'); // グーグルスプレッドシートのアドレス
  const sheet = ss.getActiveSheet();
  sheet.getRange("A1").setValue(title);
  sheet.getRange("B1").setValue(url);
  // console.log('%s ,%s', ss_title, ss_url);
}
```

スプレッドシートからタイトルとURLを読み込む関数``getSpreadsheet_value``

```ruby:getSpreadsheet_value
function getSpreadsheet_value() {
  const ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/**********/edit'); // グーグルスプレッドシートのアドレス
  const sheet = ss.getActiveSheet();
  var ss_title = sheet.getRange("A1").getDisplayValue();
  var ss_url = sheet.getRange("B1").getDisplayValue();
  // console.log('%s ,%s', ss_title, ss_url);

  var gss = {
    TITLE: ss_title,
    URL: ss_url,
  };
  return gss;
}
```

[参考にさせていただいた記事のツイート用メソッド](https://qiita.com/yuzinet/items/ae4b9ca2b5cd989de435#%E3%83%84%E3%82%A4%E3%83%BC%E3%83%88%E7%94%A8%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89)を次のように修正

```ruby:postTwitter
- function postTwitter(){
-   let twitterText = "Test tweet!!!"; // ツイートしたいメッセージを入力
+ function postTwitter(tw_title,tw_url){
+   let twitterText = tw_title+"\n"+tw_url; // ツイートしたいメッセージを入力

```

</details>

https://x.com/calcMCalcm/status/1810168941165113355