Blueskyで過去の投稿をTwilogみたいに検索したい話
Blueskyはまだ新しいサービスなので、Twitterのようには派生サービスが充実していない。Blueskyには公式の検索機能があるが、特に日本語に関してはあまり十分ではない。では、どうすればよいか。
Bluefeedを使う方法
Bluefeedはカスタムフィードを簡単に作成できるサービスだ。
Bluefeedのトップページは素っ気ない。新興ウェブサービスというのは大概そんなものなので気にせずログインしよう。パスワードにはApp Passwordを使うこと。App Passwordは日本語では「アプリパスワード」のこと。「設定」の下の方にある。
アプリパスワードとは、アプリ専用に個別に発行されるパスワードだと思えばよい。設定画面の下の方に青いボタンがあるから押すといい。
こんな感じの画面が出てくるので、Createしてやってほしい。多少失敗してもパソコンは壊れたりしないので安心してほしい。
App Passwordを発行できて、Bluefeedに無事にログインできたら、「新規フィード作成」を選ぶ。
上の方に「ポストを検索」と表示されたプルダウンメニューがあるので、そこで「自分のポストを検索」を選び、下のテキストボックスに検索語句を入力すると、ページ下部に検索結果が表示される。これで自分のポストを検索することができた。
SkyFeedを使う方法
はっきりいって上記のBluefeedの方がわかりやすいので、下記は特に薦めないが、SkyFeedを使っても同様のことができる。
トップページは素っ気ない。大概そんなものだ。先ほどと同様にApp Passwordでログインしてほしい。
SkyFeedにはFeed Builderという機能がある。詳しくは下記を読んでほしい。
以下は、Feed Builderでのカスタムフィードの使い方を理解している前提で書く。
InbutをSigle Userにして自分のDIDを入力すると、自分の投稿をすべて拾える。RegExに検索ワードを入れると自分のpostsを検索できる。このカスタムフィードは公開せずに自分だけで使うことができる。
RegExに任意のすべての文字がヒットするような語句を入れたうえでSort byをascendingにすれば、最初の投稿からズラッと自分の投稿を並べられるはずだ。
閑話休題:Twilogとは何か
ここまでBlueskyの投稿を検索する方法を2つ紹介した(ただしBluefeedのほうがわかりやすいだろうから、SkyFeedの件は蛇足だ)。実際に検索してみてほしい。しばらくすると、「そういうことじゃないんだよな」と気付くかもしれない。
たしかに、上記の方法でログを検索することはできる。しかしその結果からは、「この言葉を書いたのはいつだろう」「この言葉をどんな風に書いただろう」ということしか知ることができない。
Twilogならば、その投稿の前後に何を書いていたのかがすぐわかる。その投稿の翌日に何を書いたのかもわかる。検索からピンポイントに投稿を絞った後の展開があるのだ。また、日付を指定して「3か月前のこの日に何を書いていたのか」を知ることもできる。
残念ながら、現時点ではこうした要求を満たすサービスをわたしは知らない。しかし、別のしかたで代用することは可能だ。
RSSを取得してスプレッドシートに蓄積する方法
Twilogのようなサービスはなくても、Blueskyの投稿をログとして蓄積することはできる。スプレッドシートを使えばよい。
2024/03/17加筆:
公式RSSフィードの提供開始、下記で紹介しているBluestreamのサービス停止などを受けて、別途note記事を作成しました。
■
Blueskyの個別アカウントの投稿はRSS化することができる。
Bluestreamというサービスを使えば、どのアカウントの投稿であってもRSSフィードにすることができる。自分の投稿だけに限らない。ログインしていなくてもよい。(余談だが、アカウントを持っていなくてもBlueskyの投稿は自由に閲覧できるというのは、つまりこういうことだ。)
RSSリーダを日頃から利用している場合は、それを活用するとよいだろう。わたしはRSSリーダはもう10年以上使っていないのでよくわからない。
Googleスプレッドシートに蓄積する場合、こんな感じになる。
わたしはGoogle Apps Script(GAS)に少しだけ慣れているので、スプレッドシートとGASを使ってこのようなデータベースをつくっている。
GASの導入についてはウェブ上に無数の入門記事があるはずだし、いまならChatGPTとの対話を重ねることで初心者でもかなり容易に取り組めるはずだ。ここではGASについては紙幅を割かない。
参考に、わたしが使っているGASのコードを以下に記載する。これをトリガーで1日に3回ほど回している。わたしは職業エンジニアではないし、そうだったこともない。プロから見たら笑われるかもしれないし、うまく動く保証はない。
function bluesky_logbook() {
//RSSから情報をスプレッドシートに取得する
// Googleスプレッドシートを指定
var spreadsheetId = "hogehogehogehoge";
var sheetName1 = "RSS";
var sheetName2 = "Logbook";
var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
var sheet1 = spreadsheet.getSheetByName(sheetName1);
var sheet2 = spreadsheet.getSheetByName(sheetName2);
// RSSフィードのURLの配列を指定
var rssUrl = "https://bluestream.deno.dev/ffi.bsky.social";
// RSSからエントリを取得して蓄積
var response = UrlFetchApp.fetch(rssUrl);
var xml = response.getContentText();
var document = XmlService.parse(xml);
var channel = document.getRootElement().getChild("channel");
var items = channel.getChildren("item");
var existingLinks = sheet1.getRange(2, 1, sheet1.getLastRow() , 1).getValues();
var itemsLength = items.length -1;
for (var j = 0; j < itemsLength +1; j++) {
var k = itemsLength - j ;
var item = items[k];
var pubDate = item.getChild("pubDate").getText();
var link = item.getChild("link").getText();
var description = item.getChild("description").getText();
var title = item.getChild("title").getText();
// 既存のリンクと比較して重複があれば処理を終了
if (existingLinks.some(row => row[0] === link)) {
continue;
}
sheet1.getRange(sheet1.getLastRow() + 1, 1).setValue(link);
sheet1.getRange(sheet1.getLastRow(), 2).setValue(pubDate);
sheet1.getRange(sheet1.getLastRow(), 3).setValue(description);
sheet1.getRange(sheet1.getLastRow(), 4).setValue(title);
}
//RSSから取得したデータを読みやすいように加工
var lastRow = sheet1.getLastRow() ;
var lastRow2 = sheet2.getLastRow() ;
for (var i = lastRow2 -1; i < lastRow +1; i++) {
// 変換前の日付データを取得
var inputDate = sheet1.getRange(i, 2).getValue();
// 入力日付をDateオブジェクトに変換
const dateObject = new Date(inputDate);
// JSTに変換するために9時間を足す
dateObject.setHours(dateObject.getHours() + 9);
// 新しい日付のフォーマットを設定
const newDateFormat = "yyyy-MM-dd HH:mm:ss"; // 例: "2023-09-02 02:08:46"
// Utilities.formatDate()を使用して新しい日付フォーマットに変換
const formattedDate = Utilities.formatDate(dateObject, "GMT", newDateFormat);
// 変換された日付をセルにセット
sheet2.getRange(i, 1).setValue(formattedDate);
// post本文のテキストを取得
var cellText = sheet1.getRange(i, 3).getValue();
// 正規表現を使用して日本語の部分を抽出
var withoutTagsText = cellText.replace(/<[^>]+>/g, '');
// 抽出された日本語のテキストを別のセルに入力
sheet2.getRange(i, 2).setValue(withoutTagsText);
// <img src> タグを含むかチェック
var hasImage = /<img src="https/.test(cellText);
// セルに結果をセット
sheet2.getRange(i, 3).setValue(hasImage ? "【画像あり】" : "");
// Replyの有無がわかるセルを取得
var cellText = sheet1.getRange(i, 4).getValue();
// "Post by ffi.bsky.social"を削除
var withoutPostBy = cellText.replace(/Post by ffi\.bsky\.social,?/, '');
// "reply to" の部分を抽出し、@を追加
var replyToMatch = withoutPostBy.match(/reply to ([^.]+)\.bsky\.social/);
var formattedText = replyToMatch
? " @" + replyToMatch[1] + ".bsky.social"
: "";
// 抽出されたテキストをセルにセット
sheet2.getRange(i, 4).setValue(formattedText);
}
}
sheet1には、postのURL、日付、投稿内容の3つを蓄積している。sheet2には、JST変換した日付、投稿内容からHTMLタグを省いたもの、添付画像の有無、リプライだった場合の相手のアカウント名の4つを蓄積している。
わたしは日記を書くためにこのログデータを使っている。添付画像はたいてい自分の端末に保存されているので、「画像の有無」がわかれば済むという考えで運用している。
なお、RSSをつくるところから自分でやりたい場合は先人がいるので参考にしてほしい。わたしはそこまでのスキルはないので取り組まなかった。
また、Bluestreamを使う場合、直近50件ほどの投稿しか拾うことができないはずだ。これでは、何日も前にBlueskyを始めたときの投稿を拾えない。解決策はあるはずで、それほど難しくもないのだろう。わたしには現状そこまでのスキルはないので取り組んでいない。AT Protocolのドキュメントを読める人ならば簡単にできるのだろうとは思う。
以上が、Blueskyのログを検索するための現時点での方法だ。あるいは明日にでもBlueskyLogなる新サービスが公開され、この記事の価値はなくなるかもしれない。早くその日が来ることを願う。