寒さを吹き飛ばせ!GASとChatGPTで目指す自動投稿への挑戦🚀

おはこんばんにちは!気温が寒くなってきたことで、大好きな編み物が捗るようになって嬉しいしおりんです 🧶今は小さいバッグを製作中です💪

今回は🎅GMOペパボエンジニア Advent Calendar 2023の7日目の記事です。 昨日はエンジニアが5ヶ月で5つの部署を回ってみた|yumuでした 🎉 ペパボのエンジニア新卒研修がどういうことをしているのか気になる方はぜひ読んでみてください 😊

adventar.org

はじめに

最近MuuMuu Sitesというサービスを使ってブログのまとめサイトを作成し、その中で日記ページを開設しました 🎉

shiorin-holon.fun

日記を書いたとき見てくれる人がいると嬉しいので、公開したらXにもタイトルと日記ページのリンクを貼って投稿しています。 ...が、これを毎回手動でやるのはちょっとめんどくさい🫠

なのでこの投稿作業が自動で行われるようにGoogle Apps Script(以下GAS)で実現できないか、ChatGPTとおしゃべりしながら進めてみました 💪

完成したのでそれを紹介します!!....という記事にしたかったのですが、結論から言うと完成まで至らずまだ道半ばの状態なので、今回は試行錯誤した様子を紹介します😭

MuuMuu Sitesとは?

MuuMuu Sitesとは、ムームードメイン独自ドメインを取得しNotionのページと組み合わせることで、簡単にWebサイトとして公開できるサービスです!

muumuu-domain.com

Notionのテンプレートが充実しているため、Notionに作りたい内容を追加するだけで簡単にWebサイトを公開できるのがとても良いです。

またGMOペパボでは、入社するとムームードメインで1人1つ好きな独自ドメインを取得できるという福利厚生があります! recruit.pepabo.com

MuuMuu Sites楽しそう!福利厚生でもらえる独自ドメインもまだGetしてなかったしアプトプット力向上のために日記サイトを作りたいと思っていただよなぁ 🤔 よし!やるぞ!と思い、サービスに登録して作ってみました 💪

自動投稿をどうやって実現させるか

新しい記事を公開した際、自動的にツイートするようにするためには「新しい記事が追加されたこと」を検知する必要となります。

今回私は以下の流れで、検知からXへの投稿までの流れを実現しようと考えました。

日記ページを追加しWebページに反映する
↓
ページが追加されたタイミングで、スプレットシートに記録する
↓
スプレットシートの更新内容を見て追加があったことを検知したら、その内容をXに投稿する

この流れを実現するためにどういうことを試したかについて、次の章から説明します。

1. MuuMuu Sitesを更新するときのメールをフックとしてツイートする

まず考えていたのはMuuMuu Sitesから更新が完了した際に届くメールを検知する材料にできないかということです。

Notionでの編集が完了しWebページへ公開するためには、以下の手順を踏む必要があります。

  1. Notionのページの編集が完了したら、MuuMuu Sitesの管理ページに行き「更新する」ボタンを押す
  2. MuuMuu Sites側で追加したNotionの内容をWebページに反映するための更新処理を行う
  3. 更新が完了するとその通知がメールで届く

なので、メールの内容を取ってくるためのGASをまず相談しました。

すると、以下のスクリプトを返してくれました

function extractAndWriteToSheet() {
  // スプレッドシートのID
  var spreadsheetId = 'Your_Spreadsheet_ID';

  // スプレッドシート内のシート名
  var sheetName = 'Sheet1';

  // 検索する件名
  var subjectToSearch = 'Your_Target_Subject';

  // Gmailの検索クエリ
  var searchQuery = 'subject:' + subjectToSearch;

  // スプレッドシートを開く
  var sheet = SpreadsheetApp.openById(spreadsheetId).getSheetByName(sheetName);

  // Gmailのメッセージを検索
  var threads = GmailApp.search(searchQuery);

  // スプレッドシートをクリア
  sheet.clear();

  // ヘッダー行を追加
  sheet.appendRow(['Subject', 'Body', 'Received Time']);

  // 各スレッドのメッセージを処理
  for (var i = 0; i < threads.length; i++) {
    var messages = threads[i].getMessages();
    for (var j = 0; j < messages.length; j++) {
      // メッセージの件名、本文、受信時間を取得
      var subject = messages[j].getSubject();
      var body = messages[j].getPlainBody();
      var receivedTime = messages[j].getDate();

      // スプレッドシートに書き込む
      sheet.appendRow([subject, body, receivedTime]);
    }
  }
}

// 新しいメールが届いたときに実行するトリガーを設定
function setTrigger() {
  // スクリプトを毎分実行するトリガーを設定
  ScriptApp.newTrigger('extractAndWriteToSheet')
    .timeBased()
    .everyMinutes(1)
    .create();
}

このスクリプトを実行してみた結果が以下です。

想定した通りに取れている🙌 ... となったのですが、具体的にその時更新したページの情報まではメールの中身には載っていませんでした。 Xへポストする時に日記のタイトルとリンクを含めたいという目的が、メールを使う形だと実現できないと判断し、この方法は断念しました。

2. 日記ページのタイトルとリンクをチェックして、新しいものが追加されたことを検知したらツイートする

更新完了メールではダメそうということがわかったので、次はWebページに公開された時の変化を検知する材料として使えないか試してみることにしました。

MuuMuu Sitesで作られた日記ページを開発者ツールでみてみると、muu-database-card__titlemuu-database-card__linkにそれぞれ日記のタイトルとリンクが入っていることがわかりました。

なので、ここをスコープにすることで新しいものが追加されたことを検知できるのでは?と思い、早速ChatGPTに相談してみました。

function scrapeWebsiteAndRecord() {
  // スプレッドシートのIDとシート名を指定
  var spreadsheetId = 'YOUR_SPREADSHEET_ID'; // ここにスプレッドシートのIDを入力
  var sheetName = 'Sheet1'; // ここにシートの名前を入力

  // ウェブサイトのURLとクラスを指定
  var url = 'https://shiorin-holon.fun/acbb35e7-2c92-48a6-929d-b2bdeb55d879/';
  var titleClassName = 'muu-database-card__title';
  var linkClassName = 'muu-database-card__link';

  // スプレッドシートを取得
  var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
  var sheet = spreadsheet.getSheetByName(sheetName);

  // タイトルとリンクを取得
  var data = scrapeData(url, titleClassName, linkClassName);

  // 新しいデータがあればスプレッドシートに書き込む
  data.forEach(function (item) {
    if (!sheet.getRange('A:A').getValues().flat().includes(item.title)) {
      sheet.appendRow([new Date(), item.title, item.link]);
    }
  });
}

function scrapeData(url, titleClassName, linkClassName) {
  var response = UrlFetchApp.fetch(url);
  var html = response.getContentText();

  // HTML解析
  var doc = XmlService.parse(html);
  var root = doc.getRootElement();

  // タイトルとリンクを取得
  var titleElements = findElementsByClass(root, titleClassName);
  var linkElements = findElementsByClass(root, linkClassName);

  // 取得した要素のテキストとリンクを配列にして返す
  var data = [];
  for (var i = 0; i < titleElements.length; i++) {
    var title = titleElements[i].getText();
    var link = (linkElements[i] && linkElements[i].getAttribute('href')) ? linkElements[i].getAttribute('href').getValue() : '';
    data.push({ title: title, link: link });
  }
  return data;
}

// 特定のクラスを持つ要素を検索する関数
function findElementsByClass(element, className) {
  var elements = [];
  var descendants = element.getDescendants();
  for (var i = 0; i < descendants.length; i++) {
    var currentElement = descendants[i];
    if (
      currentElement.getType() === XmlService.ContentTypes.ELEMENT &&
      currentElement.getAttribute('class') &&
      currentElement.getAttribute('class').getValue() === className
    ) {
      elements.push(currentElement);
    }
  }
  return elements;
}

URLの形にするためには調整が必要だけれど、ひとまず日記ページごとに取得してスプレットシートに書き込むことができた!

よし!いよいよXへポストする処理を追加するぞ!とChatGPTに相談したのですが....APIに接続するためのトークンの設定まわりで手こずってしまい、うまくいきませんでした....

この辺りはXのAPIドキュメントを読み込んで仕様を読み解く必要がありそうなことが判明したので、引き続きみていこうと思います。

おわりに

ChatGPTと相談しながらコードを書くという楽しさと意図したものが返ってくるように伝える少しの大変さを実感することができたのは、とてもよかったです!

目的が達成できるようにChatGPTやGASの勉強を続けて、引き続き取り組んでいきます 💪

そして次の🎅GMOペパボエンジニア Advent Calendar 2023 - Adventarは、inowayさんです!お楽しみに 😊