🐦

【無料】X(Twitter)にTech Blog の記事をランダムに定期・自動投稿するScriptをGASで作る!

2024/01/21に公開

こんにちは、AIQ株式会社のフロントエンドエンジニアのまさぴょんです!
今回は、X(Twitter)にTech Blog の記事をランダムに定期・自動投稿するScriptをGASで作る方法について、解説します。

X(Twitter)にTech Blogの記事をランダムに定期・自動投稿したい!

私は個人Tech Blogと、Zennの2つでTech Blogの執筆をしています。
今回は、この2つのBlogのうち個人Tech Blogの方の記事をランダムに定期・自動投稿するためのScriptをGASで作成しました。

GASを選んだ理由

最初は、PythonでTech Blogの記事をランダムに定期・自動投稿するScriptを作成しようと考えていましたが、コストの面でGASを採用しました。
(GASなら、無料で簡単に定期・自動投稿するScriptを実現できます。。。🙌 🌟)

GAS採用までの調査のプロセスを記したスクラップ記事

https://zenn.dev/manase/scraps/d406ca3eeae90e

要件定義

次の内容をプログラムで自動化することを要件定義とする。

  1. Tech Blogの記事を投稿したい。
  2. 投稿する記事は、ランダムに選びたい。
  3. 毎日、朝(午前7~8時)と昼(午後11時〜12時)と夕方(午後6~7時)の3回、定期的に投稿をしたい。

【事前準備】GASとX(Twitter)APIの設定をする💪🥺💪✨

Google Apps Script(GAS)を使って、X(Twitter)に自動投稿を行うには、GASとX(Twitter)それぞれの設定が必要です。
こちらの記事を参考に、X(Twitter)APIの登録と、Google Apps Scriptのプロジェクトを作成・OAuth2ライブラリの追加やOAuth2の認証を進めてください。

GoogleのOAuth2の認証

X(Twitter)のOAuth2認証

https://prtn-life.com/blog/gas-twitter-api

スプレッドシートで投稿のMetaデータを作成する💪🥺💪✨

今回は、GASで投稿を作成するためのMetaデータ(記事一覧データ)の保管庫として、スプレッドシートを活用します。
イメージとしては、次のようなデータを作成していきます。

WordPressで、スプレッドシートに投稿のMetaデータを作成する場合

WordPressを使っている方の場合は、Export All URLsというPluginを使って、記事一覧データの抽出を実行することができます。

結果は、次のように表示されます。
これをコピペして、スプレッドシートに貼り付ければ、Metaデータ(記事一覧データ)の保管庫は完成です。

X(Twitter)にTech Blog の記事をランダムに定期・自動投稿するScript

X(Twitter)にTech Blog の記事をランダムに定期・自動投稿するScriptの内容は、次のとおりです。
こちらのGASのCodeは、こちらの『GAS×スプレッドシートでTwitterに定期自動投稿する方法』のSampleCodeをベースにハッシュタグに使用するカテゴリー情報などを追加しています。
(素晴らしい記事をありがとうございます🙌)

PostArticleAndHashTag.js
// X APIを使った OAuth2認証 & 投稿の定期実行のサンプル Ver. ハッシュタグ追加

/** ツイッターのクライアント識別子 */
const CLIENT_ID = "";
/** ツイッターのClient Secret */
const CLIENT_SECRET = "";
/** スプレッドシートのID */
const SHEET_ID = "";
/** 編集権限のある Google Acount の G_Mail */
const G_MAIL = "";

// 初回だけ必要な OAuth2・認証処理
function main() {
  const service = getService();

  Logger.log("初回だけ必要な OAuth2・認証処理 Start");

  if (service.hasAccess()) {
    Logger.log("認証済みです");
  } else {
    Logger.log("認証ができていません");
    const authorizationUrl = service.getAuthorizationUrl();
    Logger.log("次の URL を開いて、認証をします。: %s", authorizationUrl);
  }
}

// スプレッドシートからツイッターに投稿する記事をランダムに取得して、ツイート本文を作成する
function autoTweetFromSheet() {
  Logger.log("ツイート本文を作成する・処理 Start");
  let today = new Date();
  let todayStr = Utilities.formatDate(today, "JST", "YYYY/MM/dd");

  const mySpreadSheet = SpreadsheetApp.openById(SHEET_ID);
  Logger.log(mySpreadSheet);

  // 編集権限のある Google Acount の G_Mail を渡す
  mySpreadSheet.addEditor(G_MAIL);

  const sheetList = mySpreadSheet.getSheets();
  Logger.log(sheetList);

  // 1番目のスプレッドシートを取得する
  const sheet = mySpreadSheet.getSheets()[0];
  Logger.log(sheet);

  const lastRow = sheet.getLastRow();

  const targetRow = makeRundom(lastRow);

  /** タイトル */
  const title = sheet.getRange(targetRow, 1).getValue();
  /** URL */
  const link = sheet.getRange(targetRow, 2).getValue();

  /** カテゴリー(ハッシュタグに使用する) */
  const category = sheet.getRange(targetRow, 3).getValue();

  /** 投稿本文を作成する Ver. ハッシュタグ追加 */
  const msg =
    `【Tech Blog おすすめ記事紹介 ${todayStr}】\n` +
    `\n${title}\n` +
    `\n記事はこちら🌟\n${link}\n` +
    `\n#${category}\n#Web開発\n#Webエンジニア\n#エンジニアと繋がりたい\n#プログラミング`;

  return msg;
}

// 乱数作成
function makeRundom(count) {
  let random = Math.random();
  random = Math.floor(random * count) + 1;
  return random;
}

// X(Service) に X-API, OAuth2 を使って Access する
function getService() {
  pkceChallengeVerifier();
  const userProps = PropertiesService.getUserProperties();
  const scriptProps = PropertiesService.getScriptProperties();

  // X-Service作成
  return OAuth2.createService("twitter")
    .setAuthorizationBaseUrl("https://twitter.com/i/oauth2/authorize")
    .setTokenUrl(
      "https://api.twitter.com/2/oauth2/token?code_verifier=" +
        userProps.getProperty("code_verifier")
    )
    .setClientId(CLIENT_ID)
    .setClientSecret(CLIENT_SECRET)
    .setCallbackFunction("authCallback")
    .setPropertyStore(userProps)
    .setScope("users.read tweet.read tweet.write offline.access")
    .setParam("response_type", "code")
    .setParam("code_challenge_method", "S256")
    .setParam("code_challenge", userProps.getProperty("code_challenge"))
    .setTokenHeaders({
      Authorization:
        "Basic " + Utilities.base64Encode(CLIENT_ID + ":" + CLIENT_SECRET),
      "Content-Type": "application/x-www-form-urlencoded",
    });
}

// 認証後のCallBack
function authCallback(request) {
  const service = getService();
  const authorized = service.handleCallback(request);
  if (authorized) {
    return HtmlService.createHtmlOutput("Success!");
  } else {
    return HtmlService.createHtmlOutput("Denied.");
  }
}

function pkceChallengeVerifier() {
  var userProps = PropertiesService.getUserProperties();
  if (!userProps.getProperty("code_verifier")) {
    var verifier = "";
    var possible =
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";

    for (var i = 0; i < 128; i++) {
      verifier += possible.charAt(Math.floor(Math.random() * possible.length));
    }

    var sha256Hash = Utilities.computeDigest(
      Utilities.DigestAlgorithm.SHA_256,
      verifier
    );

    var challenge = Utilities.base64Encode(sha256Hash)
      .replace(/\+/g, "-")
      .replace(/\//g, "_")
      .replace(/=+$/, "");
    userProps.setProperty("code_verifier", verifier);
    userProps.setProperty("code_challenge", challenge);
  }
}

function logRedirectUri() {
  var service = getService();
  Logger.log(service.getRedirectUri());
}

// ツイートを実行する
function sendTweet() {
  const payload = {
    text: autoTweetFromSheet(),
  };
  Logger.log("ツイートを実行する");

  const service = getService();
  if (service.hasAccess()) {
    Logger.log("サービスアクセス完了 ツイート処理を続行する");

    const url = `https://api.twitter.com/2/tweets`;
    const response = UrlFetchApp.fetch(url, {
      method: "POST",
      contentType: "application/json",
      headers: {
        Authorization: "Bearer " + service.getAccessToken(),
      },
      muteHttpExceptions: true,
      payload: JSON.stringify(payload),
    });
    const result = JSON.parse(response.getContentText());
    Logger.log(JSON.stringify(result, null, 2));

    const sheet = SpreadsheetApp.openById(SHEET_ID).getSheets()[0];
    const lastRow = sheet.getLastRow();
    let now = new Date();
    sheet.getRange(lastRow + 1, 1).setValue(result["data"]["id"]);
    sheet.getRange(lastRow + 1, 2).setValue(result["data"]["text"]);
    sheet.getRange(lastRow + 1, 3).setValue(now);
  } else {
    Logger.log("認証ができていません");
    var authorizationUrl = service.getAuthorizationUrl();
    Logger.log("次の URL を開いて、認証をします。: %s", authorizationUrl);
  }
}

GASで、トリガーの設定をする

最後に「毎日、朝, 昼, 夕の3回、定期的に投稿する」ためのGAS・トリガーの設定をしていきます。
まずは、GASのトリガーを選択します。

続いて、朝, 昼, 夕の3タイプの定期実行トリガーを作成していきます。

朝(午前7~8時)のトリガー設定

昼(午後11時〜12時)のトリガー設定

夕方(午後6~7時)のトリガー設定

これで、Blogの記事をランダムに定期・自動投稿する仕組みが完成しました🙌✨

まとめ

今回の自動・定期投稿のScript作成により、おすすめ投稿を手間をかけずにできるようになりました。
やはり自動化は偉大ですね。

まさぴょんのX(Twitter)のフォロー、ぜひともお願いします。
これから、Zenn Tech Blogの方も自動投稿対象に追加する予定です。

個人で、Blogもやっています、よかったら見てみてください。
https://masanyon.com/

注意事項

この記事は、AIQ 株式会社の社員による個人の見解であり、所属する組織の公式見解ではありません。

求む、冒険者!

AIQ株式会社では、一緒に働いてくれるエンジニアを絶賛、募集しております🐱🐹✨

詳しくは、Wantedly (https://www.wantedly.com/companies/aiqlab)を見てみてください。

参考・引用

https://prtn-life.com/blog/twitter-gas-sheet

https://prtn-life.com/blog/gas-twitter-api

https://ja.wordpress.org/plugins/export-all-urls/

https://zenn.dev/manase/scraps/203a94f6ec53a0

https://zenn.dev/manase/scraps/d406ca3eeae90e

AIQ Tech Blog (有志)

Discussion