Blueskyでプロフィール検索Custom feedを実装した（Skyfeed未実装機能）

# はじめに

[Bluesky](https://bsky.app/)に移住したSuibariです。
今回、BlueskyにおけるTwitterのList的な機能である[Custom Feed](https://www.docs.bsky.app/docs/starter-templates/custom-feeds)を実装しました。

Custom Feedは以下公式のFeed Generator SDKを使うのが早道ですが、実装的にはFirehoseで全世界のポストをクエリでフィルタリングしてDBに取り込み、それを返すようになっています。
https://github.com/bluesky-social/feed-generator/blob/main/README.md

同じことは[Skyfeed](https://skyfeed.app/)などサードパーティクライアントでもノーコードで実現できるので、わざわざサーバを立てて実装する旨味があまりないです。

**今回は差別化として「全ユーザのプロフィール欄の検索を行い、hitしたユーザの全ポストをまとめて返す」feedアルゴリズムを実装しました。**
これはskyfeedにもない機能になります。

本feedを作りたかった目的として、Skyfeedの自作Custom Feedのinput feedとして設定することで、機能拡張を狙いたかったところがあります。
（しかし2024/2/18現在、[input feedに自作feedは設定できない](https://scrapbox.io/Bluesky/SkyFeed%E3%81%AEFeed_Builder%E3%81%A7%E4%BD%BF%E7%94%A8%E3%81%A7%E3%81%8D%E3%82%8B%E5%90%84Block%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)ようです。作ってから気づいた…）

# 作ったもの

作成したCustom Feedは以下です。
https://bsky.app/profile/did:plc:uixgxpiqf4i63p6rgpu7ytmx/feed/whats-alf

サンプルとして **「baystars」または「ベイスターズ」をプロフィール文に含むユーザのポストを時系列順に表示する** ようにしています。
このクエリは皆さんのサーバにデプロイさえすれば自由に変更できるので、推しの名前を設定してみるなど自由にお使いください。

# 必要なもの

* 自前サーバまたはRender.comなどのPaaSクラウド
* Blueskyアカウント

# 使い方

## feedサーバ構築

まず、以下リポジトリを自分のリポジトリにfolkしてください。
https://github.com/suibari/feed-profile-search

cloneします。

```
$ git clone https://github.com/xxx/feed-profile-search.git
※xxxは自分のユーザ名
```

以下、仮にPaaSクラウドであるRender.comにデプロイするとします。

1. Render.comでAppを新規作成
1. folkして作ったリポジトリと紐づけ
1. 以下画像の環境変数を設定。各変数の説明は以下

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110901/f37acd7d-820d-bdb7-d9f4-851f09e3ab88.png)

* FEEDGEN_APP_PASSWORD: 自分のログインパスワードもしくは[App Password](https://bsky.app/settings/app-passwords)
* FEEDGEN_HOSTNAME: サーバのホスト名。render.comならhttps://[service name].onrender.com/
* FEEDGEN_LISTENHOST: 0.0.0.0
* FEEDGEN_PORT: 443
* FEEDGEN_PUBLISHER_DID: 自分のDID。[調べ方](https://scrapbox.io/Bluesky/%E8%87%AA%E5%88%86%E3%81%AEDID%E3%82%92%E7%9F%A5%E3%82%8B%E6%96%B9%E6%B3%95)
* FEEDGEN_PUBLISHER_IDENTIFIER: 自分のハンドル名。xxx.bsky.social的なやつ
* FEEDGEN_QUERY: 検索文字列。本セクション末尾に詳細を記載

デプロイ実行します。これでfeedサーバ側は準備完了。

## Blueskyサーバへの登録

次にBlueskyサーバへのPublishを行います。

1. cloneしたディレクトリのルートに.envを置きます。内容は上記環境変数を入れます。
1. 登録するfeed名や説明文など変えたければ、./scripts/publishFeedGen.ts を適宜編集
1. 以下実行

```
$ yarn
$ yarn publishFeed
```

登録は2秒くらいで終わります。

## 環境変数FEEDGEN_QUERYの設定方法

**半角スペースで区切った単語をORで検索する仕様になっています。**

```
FEEDGEN_QUERY="baystars ベイスターズ"
→これで"baystars"と"ベイスターズ"をOR検索
```

半角スペースが入る単語には対応していません。
その他細かい仕様は[Lucene Query構文](https://learn.microsoft.com/ja-jp/azure/search/query-lucene-syntax)を見てみてください。

[公式ドキュメント](https://www.docs.bsky.app/docs/api/app-bsky-actor-search-actors)によるとLucene Query構文に従うはずなのですが、OR検索ができないっぽかったです…。
そのためOR処理は自前で実装しています。

## その他アプリケーション仕様

* FirehoseのようなリアルタイムのStreaming通信が行えないので、10分おきの定期処理としています。./src/subscription.tsで間隔は変更できますが、自己責任でおねがいします

# はまりどころ

以下、詰まりました。

* 特定ユーザの全ポストを返すAPIがなかなか見つからなかった。Blueskyにおけるfeedの定義は相当幅広く、それさえも[Author Feed](https://www.docs.bsky.app/docs/tutorials/viewing-feeds#author-feeds)と定義されていることに気づくのに時間がかかった
* Typescript初めてすぎわからんちん

あとSkyfeedで現在、input feedには「個人feed」と「公式feed」が設定不可能らしいのですが、**それ以外のfeedって何なんでしょう…。**
詳しい方いましたら教えて下さい。
feed server側のレスポンスで対応できるなら、対応できると思うので歓喜なんですが。

# 終わりに

BlueskyはTwitter API触りまくってたあの頃を思い出して最高に楽しいです。
Firehoseも無料とか凄すぎる。
私も、自分が昔やってた[勢い計測](https://qiita.com/Suibari_cha/items/bc529cd6c95b36a50e8b)とか色々やっていきたいので、みんな情報共有してがんばっていきまっしょい。