Nostrを使ってはてなスターみたいなサービスを作った話

2024/07/31に公開

はてなスターみたいに、自分のサイトにスターを付けられるサービスを作りました。

https://nikolat.github.io/makibishi/

よろしければ⭐を付けていってください。

仕組み

HTMLに指定のタグを埋め込むことで任意のサイトに簡単に設置できるようにしています。
読み込むJSファイルはjsDelivrから配信してもらっています。
GitHubにpush => npmにpublish => jsDelivrで配信
みたいな流れです。
リポジトリはこちらです。

https://github.com/nikolat/makibishi

⭐のデータをやり取りする役割はNostrリレーに担ってもらっています。
Nostrについては以下の記事を参照してください。

https://zenn.dev/kaiji/articles/e855dccba73211

https://zenn.dev/mattn/articles/cf43423178d65c

MAKIBISHIはサイトオーナーが非Nostrユーザーでも設置できますし、サイト訪問者が非Nostrユーザーでもリアクションできる(optional)のが特徴です。

私は技術的なことには明るくないので、今回はNostrを使ってMAKIBISHIを開発した経緯についてのみ記録を残すことにします。今後もNostrを使ったサービスが増えていけば楽しいですね。

開発に至った流れ

発案

2024-07-19 にこんな会話の流れから作ろうと思い立ちました。

NIPs(Nostrの仕様)にはNostrイベントに対するリアクションを規定する仕様があります。

https://github.com/nostr-protocol/nips/blob/master/25.md#nip-25

また、NostrイベントにURLの参照を含める仕様もあります。

https://github.com/nostr-protocol/nips/blob/master/24.md#tags

これを組み合わせて、URLに対するリアクションを定義できるのでは?と考えました。

{
  "kind": 7,
  "content": "⭐",
  "tags": [
    ["r", "https://example.com/"]
  ],
  ...other fields
}

実装

早速手元でイベントを発行してリレーに送信し、URLを指定してイベントをリレーから受信できることを確認しました。
できた。完成。

https://nikolat.github.io/makibishi-demo/

日本語圏のNostrユーザーの人たちにも使ってもらい、問題なく動きそうな手応えを得ました。
※実験用なので当初リレーは1つに絞って動作させました

しかしこれで本稼働させるわけにはいきません。kind 7イベントはあくまでNostrイベントに対してのみリアクションするものとして規定されており、URLに対してリアクションするのはNIPsで認められていない使い方なので、この時点では仕様違反です。
これを認めてもらうために、NIPsに仕様を提案しなければなりません。

新仕様の提案

まずはIssueで意見を募ることにしました。
kind 7イベントをそのまま流用するか、新kindを定義するべきか尋ねます。

https://github.com/nostr-protocol/nips/issues/1380

staab commented
I think repurposing kind 7 with an r tag makes perfect sense. A new kind might be better though, since you might want to query these reactions without getting all the normal ones.

スターの集計等を考えた場合には、専用イベントを新設した方がよさそうです。
相談して良かった。

新しくkind 17を新設するpull requestを作成し、新仕様を提案します。

https://github.com/nostr-protocol/nips/pull/1381

好意的に受け取っていただけました。

vitorpamplona
Normalize the URL, like on https://datatracker.ietf.org/doc/html/rfc3986#section-6.

You might also want to add guidance on fragments (can you like a section of a page?)

Also, keep in mind that there is no startsWith filter in Nostr, which means that if you want to get all reactions to a domain name (or path), you have to create individual tags for each.

URLのノーマライズに関して提言をいただいたので、仕様に反映します。
これで晴れてkind 17イベントを使えることになりました。

{
  "kind": 17,
  "content": "⭐",
  "tags": [
    ["r", "https://example.com/"]
  ],
  ...other fields
}

本公開

https://nikolat.github.io/makibishi/

https://github.com/nikolat/makibishi

GitHub Actionsを使ってnpmにpublishするよう設定します。
npmに何かを公開するのは今回が初めてなのでドキドキです。
これでjsDelivrから配信されるようになります。

https://www.npmjs.com/package/@nikolat/makibishi

日本語圏のNostrユーザーの人たちに試していただきました。
早速不具合がいくつか見つかり、早くもversionが上がることに。

<script src="https://cdn.jsdelivr.net/npm/@nikolat/makibishi@0.1.1"></script>

とりあえず現状はこんな感じです。

まとめ

Nostrを使ってシステムを構築するメリット

責任が無い

冒頭で「……してもらっています」と繰り返し書いてある通り、私自身は何も、「運営」も「運用」もしていません。ただコードを書いてGitHubにpushしただけです。
みなさんが発行したNostrイベントは、世界中のNostrリレーにばら撒かれて、複製されたり消えていったりを繰り返していくでしょう。
かつて或る人がNostrの特徴を以て「無責任分散」と称しましたが、その呼び名が、私は好きです。

謝辞

  • 仕様の相談に乗っていただいた方々
  • テストにご協力いただいた方々
  • MAKIBISHIを早速サイトに設置していただいた方々

ありがとうございます。

最後にMAKIBISHIを設置していただいた皆様のサイトをご紹介させていただきます。(敬称略)

@逆砂 参角

https://njump.me/note1utjrdd7yy7vvh75sgtqwj63lqlm27j0gxrj2q649fmf5xvu7604qd2dfd8

https://invertedtriangle358.github.io/ghost/top.html

@jiftechnify

https://njump.me/note1xghhkwlz84x6rmjm3ypj7dd2z32q6lc3vyejwdch8mclf0w4u6esjckqzn

https://jiftechnify.github.io/motherfucking-nostr-client/

@mattn

https://njump.me/note1zsa8yef036pl9fd5jwne4594aehn72g78plzlyww9085s86u0hmsqv5w7r

https://nostr.compile-error.net/

@koteitan

https://njump.me/note1veadftfl8yg7zxwgamvw66hm2hrun03a28wdvhlsq2suzyvsp5kscpt77z

https://koteitan.github.io/nostr-post-checker/

@betoneto

https://njump.me/note1h3vva4jyntys0lvxgvy35xkh67xxng6lfgq6ddu74l8229aqhvjsn52xya

https://nokakoi.com/

@OrzBruford

https://njump.me/note1ff5vphw5wugfncvu9yfaqq7s26ddy5qrzyry5yafkevhphldaexqmqmxh7

https://likebar.harisen.jp/

@showV3

https://njump.me/note1m455zeyc7klz3jqgdkugshejrx5xapxxzrresgaq7hqldnfxh6usxym76h

https://showhyuga.pages.dev/

@mono

https://njump.me/note1rfkfyvuggv5gw5s74ejue6935l9t4t94v455dgg9gvm6j7z8lvks5ezvx7

https://tsukemonogit.github.io/nostr-monoGazo-bot/

@stokragon

https://njump.me/note1pu9t8e7fpadmpgjd6sh9xgdy5eczs8wpp0vr7z9q27gmtam4098q4pns9x
https://njump.me/note1g4fhlhpswtleq4mjqvxd5zcpt3j5twlk2mjmrtupxt6wmnx0mzxs9gegpn

https://stok33.github.io/
https://stok33.github.io/NostrIllustStock/
https://stok33.github.io/freeillusts/

Discussion