見出し画像

Bluesky用超簡易版自動投稿ツールを作った


私はX(Twitter)では、もっぱらスプレッドシートベースのツールを使って、自動投稿をしています。
で、最近注目を集めているSNS、「Bluesky」でも同じことができないかな、と思いBluesky用の超簡易版自動投稿ツールを作ってみました。

では早速作り方…の説明の前に。
本記事では、スプレッドシートとGAS(Google Apps Script)がある程度使える人向けという前提で話を進めます。最初から始めると長くなりそうなので。

準備

シートを作成する

シート例

投稿するポストを保存しておく表です。今回のツールは1行目を表題欄にしています。なので1行目は投稿データにはなりません。
A列は投稿したい文章(例として黄道12星座を入れています)
B列はスクリプトを実行すると、その時の日付と時刻が自動で入力される列なので、空欄のままでいいです。

ここで注意して欲しいのが、シートの名前です。スプレッドシートは新規作成すると「シート1」というシートがデフォルトで一枚作成されるだけなので、このままでいいという方は先に進んでいいのですが、シートの名前を変えたい場合は、スクリプトの記述も修正が必要です。
後述のコードを見てもらえばわかる通り、私の場合は「postリスト」に変えています。

あと、D列に「強制実行」と書いた四角がありますが、これは私が手動で動作確認したくて作った「無理やりスクリプトを走らせる」ボタンですので、別に作らなくてもいいです。

USER_IDとAPP_PASSWORDを確認する

Blueskyに外部アプリから投稿するには、上記の2つの情報をスプレッドシートに入力する必要があります。
USER_ID
プロフィールから確認できます。ご自分のアイコンとニックネームの真下に表示されている、「@xxxx.bsky.social」です。ただし、入力するのはxxxx.bsky.socialの部分です。@は入力しないでください。
APP_PASSWORD
Blueskyにツールを使うためのパスワードを発行してもらいます。Blueskyにログインするためのパスワードではありません。
発行手順は以下の通りです。

  1. 「設定」(歯車マーク)をクリック

  2. 下にスクロールして「高度な設定」の「アプリパスワード」をクリック
    「アプリパスワードを追加」をクリック

  3. アプリパスワード用の固有の「名前」を入力(英数字、スペース、ハイフン、アンダースコアのみが使用可能。長さは4文字以上32文字以下)

  4. 「アプリパスワードを作成」をクリック

  5. パスワードが表示されるのでこれをコピペして保存

  6. 「完了」をクリックして終了

注意:アプリパスワードは5.の段階でしか表示されないので、その時点でコピペを忘れないようにしてください。忘れると、1.からやり直しです。

App Scriptを開く

説明するまでもありませんが一応。ツールバーのメニューから「拡張機能」→「App Script」をクリックします。

USER_IDとAPP_PASSWORDを入力する

開いたスクリプトエディタの左側にある歯車マークをクリックします。プロジェクトの設定画面が開きます。

プロジェクトの設定画面

下にスクロールして「スクリプトプロパティ」の下側にある「スクリプトプロパティを編集」ボタンを2回クリックします。(USER_IDとAPP_PASSWORDの2項目を入力する必要があるため)
画像の要領でUSER_IDとAPP_PASSWORDを入力します。

USER_IDとAPP_PASSWORDの入力画面

コードの入力

スクリプトエディタに戻り、下記のコードをコピペします。デフォルトでは短いコードが書かれていますが、上書きで構いません。

/**
 * 「postリスト」からランダムに投稿文を抽出します。
 */
function getMoji() {
  //シートを取得
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  //postリストを参照
  var sheet = ss.getSheetByName('postリスト');
  //データのあるセルの内、一番下にあるセルの行番号を取得
  var rows = sheet.getLastRow();
  //乱数生成
  var suuji = Math.floor(Math.random() * (rows - 1)) + 2;
  //投稿内容のセルを参照
  var postMessageCell = sheet.getRange(suuji, 1).getValue();
  //文字列化
  var moji = String(postMessageCell);
  bluesky_Api(moji); // bluesky_Api関数を呼び出し、mojiを引数として渡す
  timestamp(moji, suuji, sheet); // timestamp関数を呼び出し、moji,suuji,sheetを引数として渡す
}

// スクリプトプロパティからユーザーIDとアプリパスワードを取得します。
const USER_ID = PropertiesService.getScriptProperties().getProperty("USER_ID");
const APP_PASSWORD = PropertiesService.getScriptProperties().getProperty("APP_PASSWORD");

/**
 * APIを呼び出してセッションを作成し、フィードに投稿します。
 */
function bluesky_Api(moji) {
  const post_text = moji;
  const sessionToken = createSession();
  if (sessionToken) {
    postToFeed(sessionToken, post_text);
  }
}

/**
 * セッションを作成するためのAPIを呼び出します。
 * @return {string} アクセスJWTトークン
 */
function createSession() {
  const url = 'https://bsky.social/xrpc/com.atproto.server.createSession';
  const payload = {
    "identifier": USER_ID,
    "password": APP_PASSWORD
  };
  const options = {
    'method': 'post',
    'contentType': 'application/json',
    'payload': JSON.stringify(payload)
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    const content = response.getContentText();
    const json = JSON.parse(content);
    Logger.log(json.accessJwt);
    return json.accessJwt;
  } catch (error) {
    Logger.log(error.toString());
    return null;
  }
}

/**
 * フィードに投稿するためのAPIを呼び出します。
 * @param {string} accessJwt アクセスJWTトークン
 * @param {string} post_text 投稿する文字列
 */
function postToFeed(accessJwt, post_text) {
  const url = 'https://bsky.social/xrpc/com.atproto.repo.createRecord';
  const payload = {
    "repo": USER_ID,
    "collection": "app.bsky.feed.post",
    "record": {
      "text": post_text,
      "createdAt": new Date().toISOString()
    }
  };
  const options = {
    'method': 'post',
    'headers': {
      'Authorization': 'Bearer ' + accessJwt
    },
    'contentType': 'application/json',
    'payload': JSON.stringify(payload)
  };

  try {
    const response = UrlFetchApp.fetch(url, options);
    const content = response.getContentText();
    const json = JSON.parse(content);
    Logger.log(json);
  } catch (error) {
    Logger.log(error.toString());
  }
}

/**
 * 投稿した日時をシートに書き込みます。
 */
function timestamp(moji, suuji, sheet) {
  // 現在の日付を取得
  var today = new Date();
  // 指定された行のB列に値を設定
  sheet.getRange(suuji, 2).setValue(today);
  // C2セルにmojiを設定
  sheet.getRange("C2").setValue(moji);
}

トリガーの設定

スクリプトエディタの左側にある「トリガー」をクリックします。
トリガー設定画面が開きますので右下の「トリガーを追加」をクリックします。

トリガー設定画面

トリガー追加画面が開きますので下の画像の通り設定します。このうち
「時間ベースのトリガーのタイプを選択」
「時間の間隔を選択」だけが任意です。(1時間に1回とかでも可)
Xでは投稿間隔にリミットがあったはずですが(確か30分)、Blueskyはよくわかりません。まあ、ほかのユーザーにウザがられかねないので、あまり短時間での連投は控えたほうがいいと思います。

トリガー追加画面

設定が終わったら右下の「保存」をクリックします。
スプレッドシートを閉じて準備完了です。
通常スプレッドシートはGoogleドライブに保存されますが、それで問題ありません。

以上で最低限の機能を持ったBluesky用の自動投稿ツールの完成です。
もっともこれはあくまでもシートからランダムに投稿文を抽出してBlueskyに投稿する最低限のツールなので、まだまだ改善の余地はあると考えています。(同じ文面を連投する可能性もあります)
私よりもGASに詳しい方はごまんといると思いますので、独自にコードを改良して、思うように動作するツールを作っていただければと思います。

SPECIAL THANKS

最も参考にさせていただきました。(というか、後半はほぼパクリです)この場を借りて厚く御礼申し上げます。
あと、ChatGPTにも質問しまくりました。AIがなかったら、とっくにお手上げだったと思います。


この記事が気に入ったらサポートをしてみませんか?