💨

Next.jsのSSGで作ってる個人ブログをFediverse対応させてつぶやいてみた

2023/07/31に公開

今回は、こちらの記事を参考にさせていただき、自分の作ってるサイトでもFediverse対応してみました。

https://blog.tyage.net/post/2023/2023-07-17-bridgy-fed/

できたこと

  • Next.js(SSG)で作ってるサイト(デプロイ先はVercel)をFediverse対応させて、デプロイ時にBridgy Fed経由でActivityPubで投稿が配信されるようにしてみた

やったこと

Bridgy Fed 対応

↑の参考サイトを見つつ、https://fed.brid.gy/web-site で、自分で持ってるサイトのURLを入れると導入方法を提示してくれます。

/.well-known/host-meta と /.well-known/webfinger を Bridgy Fed にリダイレクトさせる

next.config.jsに以下を追加

next.config.js
const config = {
  async redirects() {
    return [
      {
        source: '/.well-known/host-meta',
        destination: 'https://fed.brid.gy/.well-known/host-meta',
        permanent: false,
      },
      {
        source: '/.well-known/webfinger',
        destination: 'https://fed.brid.gy/.well-known/webfinger',
        permanent: false,
      },
    ]
  }

サイトトップに、h-card 追加

https://indieweb.org/h-card

    <a
      href="https://dl10yr.com/"
      className="h-card"
      rel="me"
    >
      dl10yr
    </a>

投稿部分にクラスを足していき、micfroformatsのh-entryに対応させる

h-entryで投稿を作成します。

https://indieweb.org/h-entry

完全には理解していないですが、この辺が参考になりました。

https://indieweb.org/post-type-discovery#Algorithm

下の例のようにやると、とりあえずMastdonで投稿内容が表示できました。

https://dl10yr.com/note/2023/07/31/1

/app/note/[year]/[month]/[day]/[slug].tsx
export default async function Page({ params }) {
  const note = fetchNoteDetailData(params.year, params.month, params.day, params.slug) // mdファイルを取得

  return (
    <article className="h-entry">
        <div className="e-content">{note.content}</div>
      <div className="hidden">
        <div className="p-name">{note.content}</div>
        <a className="u-bridgy-fed" href="https://fed.brid.gy"></a>
        <p className="dt-published">{note.date}</p>
      </div>
    </article>
  )
}

Webmentionを送ってBridgy Fedに通知

curl https://fed.brid.gy/webmention -d source=https://dl10yr.com/note/... -d target=https://fed.brid.gy

を送って通知します。
GitHub Actionsでmainブランチにpush時に実行されるようにしました。
変更が入ったファイルが今回の投稿用のmarkdownファイル(/_notesディレクトリ以下のファイル)が含まれていれば、そのファイル名からURLを組み立てて、curlを実行するようにしました。

.github/workflows/send-webmention.yaml
name: send webmention

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          submodules: false
          fetch-depth: 0

      - name: Send webmention
        uses: actions/setup-node@v3
      - run: git fetch origin ${{ github.event.before }} # 直前のコミットハッシュをフェッチします。
      - run: git fetch origin ${{ github.sha }} # 最新のコミットハッシュをフェッチします。
      - run: node tools/sendWebmention.mjs ${{ github.event.before }} ${{ github.sha }}

sleepさせているのは、Vercelのデプロイが終わるのを待つためです。
投稿ファイルに変更がなければ、curlを実行することはないので、curlを実行するときだけsleepしてます。

tools/sendWebmention.mjs
const child_process = await import('child_process')

const MAX_MENTION_SENDS = 10

const sleep = (sec) => new Promise((res) => setTimeout(res, sec * 1000))

async function sendWebmention(oldCommitHash, newCommitHash) {
  const diff = child_process
    .spawnSync('git', ['diff', '--name-only', oldCommitHash, newCommitHash])
    .stdout.toString()
  const files = diff.split('\n')

  const filtered = files.filter((file) => file.indexOf('_notes/') !== -1)

  if (filtered.length >= 1) {
    await sleep(110)
  }

  for (let i = 0; i < filtered.length && i < MAX_MENTION_SENDS; ++i) {
    const filePath = filtered[i].split('/')
    const linkPath = filePath.slice(1, -1)
    const url = 'https://dl10yr.com/note/' + linkPath.join('/')
    // eslint-disable-next-line no-console
    console.log('sending webmention of ' + url)
    child_process.spawnSync('curl', [
      'https://fed.brid.gy/webmention',
      '-d',
      `source=${encodeURIComponent(url)}`,
      '-d',
      `target=https://fed.brid.gy`,
    ])
  }
}

const args = process.argv.slice(2)
sendWebmention(args[0], args[1])

今後のToDo

  • Postの消去
    ここ見る限り、404返すようにして、同じようにwebmention送れば良さそうだけど、送っても上手くいかない... 410のほうが良いのかな? 消去できないと積極的には使えないかなー
    https://fed.brid.gy/docs#delete
curl https://fed.brid.gy/webmention -d source=https://dl10yr.com/note/... -d target=https://fed.brid.gy
  • フォローとか
    これもやりたい
  • 画像投稿
    これはmicroformatsのクラス足すだけでできるはず
  • リプライ、Favの表示
    webmention.jsを使うらしい?

Discussion