Bash と Caddy サーバで ActivityPub のインスタンス(4) 他のインスタンス(Mastodon系)の投稿に like する

## 注意
以下の記事は Mastodon 系(Mastodon, Pleroma など)のみで可能。Misskey 系には別に記す。

## 他インスタンスのアクターの投稿に like してみる

他人の投稿に like するには、その投稿の ID を object とする "type":"Create" の json をボディとする HTTP リクエストを、その投稿者の inbox に POST する。
具体的には
```json
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://ホニャララ/users/aktor#likes/***",
  "type": "Like",
  "actor": "https://ホニャララ/users/aktor",
  "object": "like したい投稿の ID"
}
```
を like 対象投稿をした投稿者の inbox (もしくは投稿者の所属するインスタンスの sharedInbox)に POST する。
Mastodon だと投稿の ID はその投稿をクリックするとアドレスバーに現れる。
ただし大抵は
`https://投稿者のインスタンス/@投稿者/111754832049097288`
など、エイリアスで現れる。これは
`https://投稿者のインスタンス/users/投稿者/statuses/111754832049097288`
のエイリアスである。
（試しに
`https://投稿者のインスタンス/@投稿者/111754832049097288.json`
もしくは
`https://投稿者のインスタンス/users/投稿者/statuses/111754832049097288.json`
と .json を加えると json 形式で投稿を見ることが出来る）
これを上の json の "like したい投稿の ID" に入れれば良い。
```json
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://ホニャララ/users/aktor#likes/***",
  "type": "Like",
  "actor": "https://ホニャララ/users/aktor",
  "object": "https://投稿者のインスタンス/users/投稿者/statuses/111754832049097288"
}
```
こうなる。***の部分は、重複しなければ（ID として働けば）何でも良いらしい。これにも Unixtime を使うことにする。
まずは以下の json ファイル・like.json を作る。
```json:like.json
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://{my_instance}/users/{myself}#likes/{id}",
  "type": "Like",
  "actor": "https://{my_instance}/users/{myself}",
  "object": "{target_post}"
}
```
このファイルを
{my_instance} => ホニャララ
{myself} => aktor
{id} => unixtime
{target_post} => like したい投稿
に置き換え、念の為に他の多くのインスタンスと同様に一列化して、適切なヘッダを加え curl -POST で送れば良い。
これらの置換には sed を使うことにする。
```
sed 's/{置き換える対象}/{置換後の文字列}/g'
```
が基本形だ。
```
unixtime=$(date "+%s")
myself=aktor
my_instance=ホニャララ
target_post="like したい投稿のID"

sed -e "s/{id}/$unixtime/g" like.json > live
sed -i "s/{myself}/$myself/g" live
sed -i "s/{my_instance}/$my_instance/g" live
sed -i "s|{target_post}|$target_post|g" live # url には "/" が含まれるので区切り文字を "|" に変える
```
でリクエストボディにする live ファイルが出来る。

```
body=$(cat live | jq -c .)
```
で一列化した body になる。(最後のドットを忘れてはいけない)
これのダイジェスト値は
```
echo -n "$body" | sha256sum | xxd -r -p | base64
```
で求まるのでこれを digest_val という変数で保存する。
```
digest_val=$(echo -n "$body" | sha256sum | xxd -r -p | base64)
```
日付は date_now という変数に取る。
```
date_now=$(date -uR  | head -c -6 | sed -e 's/$/GMT/')
```
取り敢えず Mastodon 系はリクエストヘッダの Digest と Date を秘密鍵で署名したものを Signature タグに記せば検証を通すので、この2つを署名し、sig_val という変数に保持する。
が、失敗した時に見返すため、オンメモリだけにせず、ファイルにも署名用ヘッダを並べることにする。
```
4_signature_heads="date: $date_now\ndigest: SHA-256=$digest_val"
echo -e "$4_signature_headers" > 4_sig_headers
sig_val=$(echo -en $4_signature_headers | openssl dgst -binary -sign private -sha256 | openssl enc -A -base64)
signature="Signature: keyId=\"https://${my_instance}/users/${myself}#main-key\",algorithm=\"rsa-sha256\",headers=\"date digest\",signature=\"$sig_val\""
```

openssl でダイジェスト・署名値を求める過程は拙記事(https://qiita.com/tochu-cha/items/1d98c4f527b168b5cdca )を見ていただきたい。

あとはこれらのヘッダを指定して curl で 投稿者のinboxへ POST すれば良い。以下にスクリプトの形にしたものを記す。
```bash:post_like.sh
#!/bin/bash


target_post="$1" # 第1引数で like する投稿の ID を
inbox_url="$2" # 第2引数で投稿者の inbox を指定

unixtime=$(date "+%s")
myself="aktor"
my_instance="ホニャララ"

sed -e "s/{id}/$unixtime/g" like.json > live
sed -i "s/{myself}/$myself/g" live
sed -i "s/{my_instance}/$my_instance/g" live
sed -i "s|{target_post}|$target_post|g" live # url には "/" が含まれるので区切り文字を "|" に変える

body=$(cat live | jq -c .)
digest_val=$(echo -n "$body" | sha256sum | xxd -r -p | base64)
date_now=$(date -uR  | head -c -6 | sed -e 's/$/GMT/')

signature_headers="date: $date_now\ndigest: SHA-256=$digest_val"
echo -e "$signature_headers" > 4_sig_headers
sig_val=$(echo -en $signature_headers | openssl dgst -binary -sign ./personal/private -sha256 | openssl enc -A -base64)
signature="Signature: keyId=\"https://${my_instance}/users/${myself}#main-key\",algorithm=\"rsa-sha256\",headers=\"date digest\",signature=\"$sig_val\""

curl -H "Date: $date_now" -H "Digest: SHA-256=$digest_val" -H "$signature" "$inbox_url" -d "$body"
```
このスクリプトを
```bash
bash post_like.sh "like したい投稿のID(https://投稿者のインスタンス/users/投稿者/statuses/***)" "投稿者のinbox(https://投稿者のインスタンス/users/投稿者/inbox)"
```
と実行すると目標の投稿に like 出来る。


## 参考記事
[ Bash (and more) で HTTP 署名とその検証 ](https://qiita.com/tochu-cha/items/1d98c4f527b168b5cdca)