こんにちは / はじめまして、So Asano です。
このたび、AT Protocol(Bluesky の基盤技術、以下 atproto)で新しいアプリをつくりました。
その名も SkyBeMoreBlue です。
👉 アプリはこちら → https://www.skybemoreblue.com/
なんで「紹介」なの?
「紹介文」と聞くと、mixi のプロフィールにあった紹介文を思い出す方もいるかもしれません。
まあ、正直、このアイデアはそこからヒントを得ています。
でもたとえばいまの X(旧 Twitter) にはそういう仕組みはありません。また、Bluesky にも(自分の知る限りでは)まだ存在していませんでした。
じゃあ「懐かしの mixi 的なユーザ同士で紹介しあう文化を、atproto 上で再解釈したらどうなるかな?」と思って、作ってみたのが SkyBeMoreBlue です。
できること
-
紹介
ユーザを対象に紹介文とタグを添えられる -
紹介の却下
自分に届いた紹介をリジェクトできる -
紹介リクエスト
ユーザに「紹介して!」とお願いできる
フォローやおすすめとはまた違う、人と人とのつながりを前に出す仕組みになっています。
レキシコン設計
SkyBeMoreBlue では 3 種類のレキシコンを用意しました。
名前空間は com.skybemoreblue.intro
にまとめています。
-
com.skybemoreblue.intro.introduction
紹介そのものを表すレコード(重複を防ぐためにユーザの DID が rkey になる) -
com.skybemoreblue.intro.rejection
届いた紹介を却下するレコード(対象のレコードへの参照を含むレコードがつくられる。元のレコードは残るが、却下レコードが存在する場合はアプリ上で紹介が非表示になる) -
com.skybemoreblue.intro.request
紹介をお願いするリクエストレコード(ユーザの DID を含むレコードがつくられる)
例えば紹介のレキシコンはこんな感じです👇
{
"lexicon": 1,
"id": "com.skybemoreblue.intro.introduction",
"defs": {
"main": {
"type": "record",
"description": "A record to introduce one user to the community",
"key": "any",
"record": {
"type": "object",
"required": ["subject", "body", "createdAt"],
"properties": {
"subject": { "type": "string", "format": "did" },
"body": { "type": "string", "maxLength": 3000, "maxGraphemes": 300 },
"tags": {
"type": "array",
"items": { "type": "string", "maxLength": 200, "maxGraphemes": 20 },
"maxLength": 10
},
"lang": { "type": "string", "format": "language" },
"createdAt": { "type": "string", "format": "datetime" }
}
}
}
}
}
Jetstream と DB
バックエンドでは Jetstream を購読して、上記の 3 種類のレコードを選択的に受け取り、Postgres に保存しています。
- Render 上の Node.js サーバーが Jetstream に接続
-
com.skybemoreblue.intro.introduction
/com.skybemoreblue.intro.rejection
/com.skybemoreblue.intro.request
を wantedCollections に指定 - レコードごとにイベント(create/update/delete)を受け取りPrisma 経由で DB に書き込み
こうすることで、Bluesky ネットワーク全体と同期しながら、SkyBeMoreBlue 独自の体験を作っています。
atproto アプリなので、データの所有権はユーザにあります(だから他人の紹介レコードは却下できても削除はできない)。また、データは SkyBeMoreBlue 上でだけつくられるとはかぎりません。このあたりが atproto のおもしろいところであり、また、atproto アプリをつくる難しいところだとおもいます。
技術スタック
- Next.js – UI と SSR、API
- Vercel – フロントエンドのホスティング + API バックエンド
- Render – Jetstream 購読などの常時接続処理
- Node.js – バックエンド実装
- Postgres (Prisma) – データ管理
Vercel は「フロントエンドと API」、Render は「常時接続してデータ処理用」という分担になっています。Vercel と Render は Postgres を介してつながっているだけで、直接のやりとりはしません。このへんがちょっとおもしろいな、ってじぶんでおもっているところです。
Linkat との連携
SkyBeMoreBlue はきずかさん作の Linkat(https://linkat.blue)とも連携しています。SkyBeMoreBlue のユーザのプロフィール画面には Linkat に登録されたリンクが表示されます。
ここでのポイントは、Linkat に API などを提供してもらっているわけではない、というところです。Linkat も atproto アプリなので、SkyBeMoreBlue 側ではそのユーザの PDS から blue.linkat.board
のレコード(それが存在すれば)を取得するだけですむわけです。このあたりに atproto における “分散” の思想をかんじてもらえたらとおもいます(まあぼくがそれをじゅうぶんに理解しているとはいえませんが)。
その他いろいろ
その他のはなしをすこしばかり。
-
6 ヶ国語対応
i18n は面倒でしたが、海外でも利用してほしいという強い思いがあり、がんばってみました。でまあ、どうせ対応するなら英日といわず、6 ヶ国語くらいやってやるか、と。いまは AI があるので以前とくらべれば労力は相当へったかなとはおもいますね。まあフランス語とかドイツ語にただしく翻訳されてるのかは謎ですけど。 -
ダークモード対応
いまどきはないとディスられるレベルのダークモード対応ですが、やっぱり確認がめんどうでしたね。Tailwind や shadcn/ui のおかげで楽だったとはいえますが。 -
AI モデレーション
投稿される紹介文には AI モデレーションを入れています。試行錯誤した結果、”omni-moderation-latest” をつかっているのですが、おもったより機能してないかんじですね。
ただし、atproto の場合、別アプリから直接レコードを作られたらなんでもかけてしまう、という問題はあります(そのため、却下機能が大事になってくるわけですが)。 -
Vibe Coding の試行錯誤
今回、開発では Claude Code を全面的につかってみたのですが、そのせいで逆に手間取る場面もありました。なんでそこがこうなるんだよ(まあぼくの命令の仕方がわるいんでしょうけど)とか、じぶんでコードかいたほうがはやいんじゃないか(イライラするとこうおもいはじめる)、という局面もおおかったですね。いずれにせよ、いい勉強にはなったかなとおもってます。
おわりに
SkyBeMoreBlue は、「紹介」という仕組みをきっかけに、Bluesky をもっと楽しくするアプリです。
紹介によるあたらしいつながりを atproto 上につくりだすことで、Bluesky がより活性化することを願っています。べつに打倒 X とはいわんですが、あれがマイクロブログの未来であってほしい気はあんまりしないですからね。
ぜひ SkyBeMoreBlue にアクセスして、自分のつながりを広げてみてください。Bluesky のアカウントをもってないひとはぜひつくってみてね。
👉 https://www.skybemoreblue.com/
なお、今回のアプリをつくるにあたり、山貂さんからは atproto にかんする大いなる知見を、ゆーさんからはレキシコン解決についてご助言をいただきました。改めてここでお礼を申し上げます。
しかしまあ、触れば触るほど atproto なんもわからんってかんじです。次はもっといいアプリがつくれたらなあ。
読んでいただきありがとうございました! みなさんもぜひ atproto に興味をもってみてくださいね。
ではまた。
Views: 1