0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Bash と Caddy サーバで ActivityPub のインスタンス(6) 他のインスタンスのアクターにリプライを送る

Last updated at Posted at 2024-01-16

やること

他のインスタンスのアクターにリプライを送る。

ActivityPub でリプライを送るとは

詳しくは Fediverse入門―非中央集権型SNSサーバを作ろう!をご一読を。簡単に要約すると

  • 自分の投稿したい文章を json 化したオブジェクトを作り
  • それを type が Create の json オブジェクトに包んで
  • リプライを送る相手とフォロワーの inbox に叩きつける

である。ただしここでは公開範囲は考えていない。
なので初めに Note オブジェクトを作ってから、 Create オブジェクトにするのが正道だと思うが、最初から Create にした json を作ってその各要素を置換する、という方法を取る。何故なら面倒だから。そして各フォロワーにも配送するのは後に回す。どうせまだいないはずだし。

reply.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 を作る。

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 が上手く作動しないので、その場合は "\/" とエスケープする。

参考記事

Fediverse入門―非中央集権型SNSサーバを作ろう!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?