やること
他のインスタンスのアクターにリプライを送る。
ActivityPub でリプライを送るとは
詳しくは Fediverse入門―非中央集権型SNSサーバを作ろう!をご一読を。簡単に要約すると
- 自分の投稿したい文章を json 化したオブジェクトを作り
- それを type が Create の json オブジェクトに包んで
- リプライを送る相手とフォロワーの inbox に叩きつける
である。ただしここでは公開範囲は考えていない。
なので初めに Note オブジェクトを作ってから、 Create オブジェクトにするのが正道だと思うが、最初から Create にした json を作ってその各要素を置換する、という方法を取る。何故なら面倒だから。そして各フォロワーにも配送するのは後に回す。どうせまだいないはずだし。
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://{my_instance}/users/{myself}/statuses/{id}/activity",
"type": "Create",
"actor": "https://{my_instance}/users/{myself}",
"published": "{date_now}",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://{my_instance}/users/{myself}/followers",
"{URL}"
],
"object": {
"id": "https://{my_instance}/users/{myself}/statuses/{id}",
"type": "Note",
"inReplyTo": {id_of_reply_to},
"published": "{date_now}",
"attributedTo": "https://{my_instance}/users/{myself}",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://{my_instance}/users/{myself}/followers",
"{URL}"
],
"content": "<p><span class=\"h-card\" translate=\"no\"><a href=\"{URL}\" class=\"u-{URL} mention\">@<span>{to}</span></a></span> {content}</p>",
"tag": [
{
"type": "Mention",
"href": "{URL}",
"name": "@{to}@{to_instance}"
}
]
}
}
この json の波括弧付きの部分を適切なものに置換し、curl で POST する。{myself} などは like と変わらない。その他
{date_now} => Note Create の作成時刻
{to} => リプライを送る相手のアクター名
{to_instance} => 相手のインスタンス
{URL} => 送る相手のプロフィールページの URL
{content} => 送る文章
に置換して送りつける json に変える。
"inReplyTo": {id_of_reply_to}
は何かというと、ただ相手にメンションするだけならこのタグには null (ダブルクォーテーションなし)でいいが、相手の Note に対してのリプライならばその Note の ID を入れるタグである。
スクリプトを作って実行、その他
前回の post_like.sh を少々変えて reply.sh を作る。
#!/bin/bash
# 自分のインスタンスとアクター名を @アクター名@インスタンス名 で入力
read -p "@myself@my_instance: " MY
MY_arry=(${MY//@/ }) # @ で MY を分割し、配列に入れる
myself=${MY_arry[0]}
my_instance=${MY_arry[1]}
echo
# reply したい相手のインスタンスとアクター名を @アクター名@インスタンス名 で入力
read -p "@to@to_instance: " TO
TO_arry=(${TO//@/ })
to=${TO_arry[0]}
to_instance=${TO_arry[1]}
echo
# reply したい投稿の ID を入力
read -p "which id do you wanna reply?: " id_of_reply_to
echo
if [ -n "$id_of_reply_to" ]; then
id_of_reply_to=\"$id_of_reply_to\" # id_of_reply_to が空でなければ DQ を付ける
else id_of_reply_to="null"
fi
# reply の内容
read -p "what do you wanna mention?: " content
echo
unixtime=$(date "+%s")
date_now=$(date -uR | head -c -6 | sed -e 's/$/GMT/')
# 相手のインスタンス・アクター名から URL と inbox を取得。また " を外しておく
URL=$(curl https://$to_instance/.well-known/webfinger?resource=acct:$to@$to_instance | jq -r '.links[] | select (.rel == "self")'.href | tr -d "\"")
echo $URL # 4debug
INBOX=$(curl -H "Accept: application/activity+json" $URL | jq .inbox | tr -d "\"")
# request-target は https://インスタンス 以下である(よって最初に "/" を付ける)
REQUEST_TARGET=$(echo "/"$(sed -E 's/^.*(https):\/\/([^/]+).([^/]+)/\3/g' <<< $INBOX))
sed -e "s/{id}/$unixtime/g" reply.json > live
sed -i "s/{myself}/$myself/g" live
sed -i "s/{my_instance}/$my_instance/g" live
sed -i "s/{to}/$to/g" live
sed -i "s/{to_instance}/$to_instance/g" live
sed -i "s/{date_now}/$date_now/g" live
sed -i "s|{id_of_reply_to}|$id_of_reply_to|g" live # / が入ってるので区切り文字を変える
sed -i "s|{URL}|"$URL"|g" live # 同上
sed -i "s/{content}/$content/g" live # この方法だと content に / が入っていたらバグる
body=$(cat live | jq -c .)
digest_val=$(echo -n "$body" | sha256sum | xxd -r -p | base64)
signature_headers="(request-target): post ${REQUEST_TARGET}\ndate: ${date_now}\nhost: ${to_instance}\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=\"(request-target) date host digest\",signature=\"$sig_val\""
curl -H "Date: $date_now" -H "Digest: SHA-256=$digest_val" -H "$signature" $INBOX -d "$body"
echo $body | jq .object > ./statuses/$unixtime
echo $body > ./post/$unixtime
これでメンション・リプライが送れるが、後のことを考えて Note オブジェクトを取っておくことにした。
なので送る Create の object タグの内容を statuses というディレクトリに保存する。
また、送る json その物も post ディレクトリに保持することにする。
mkdir statuses post
そして reply.sh の最後に
echo $body | jq .object > ./statuses/$unixtime
cho $body > ./post/$unixtime
を付け加えた。
また、コード内にコメントしたようにリプライ内容に "/" (スラッシュ)が入っていると sed の区切り文字と重複して sed が上手く作動しないので、その場合は "\/" とエスケープする。