Skip to content

Instantly share code, notes, and snippets.

@hyuki
Last active December 2, 2022 03:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hyuki/01e1981d0a519f78779139287a1f235e to your computer and use it in GitHub Desktop.
Save hyuki/01e1981d0a519f78779139287a1f235e to your computer and use it in GitHub Desktop.
静的ファイルでMastodonにリプライを送る(How to implement a basic ActivityPub serverを実際にやってみた)

概要

静的ファイルと小さなRubyスクリプトを使ってMastodonの投稿に対してリプライを送るサンプルコードです。

これは、2018年にEugen Rochkoさんが書いたHow to implement a basic ActivityPub serverを実際にやってみたものです。Eugenさんの記事はActivityPubの小さなチュートリアルになっています。

なお、Eugenさんの記事のままだとDigestフィールドが足りないので、deliver.rbではそこを修正しています。

参照

手順

まず、Eugenさんの記事How to implement a basic ActivityPub server - Official Mastodon Blogを読みます。

Webサイトを用意します。私はhttps://mono.hyuki.net/を使っていますが、もちろん自分が管理しているWebサイトを使います。

actorを適宜書き換えてhttps://mono.hyuki.net/actorに設置します。publicKeyPemは以下の手順で作成したpublic.pemの内容を書きますが、改行は\nという二文字に置き換えます。

openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem

webfingerを適宜書き換えてhttps://mono.hyuki.net/.well-known/webfingerに設置します。

create-hello-world.jsonを適宜書き換えますが、Webサイトに設置する必要はありません。deliver.rbからアクセスできる場所に置いておきます。

deliver.rbを適宜書き換えてからruby deliver.rbを実行します。これで、create-hello-world.jsonで指定したMastodonの投稿にHello worldというリプライが送られます(下図参照)。

image

{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1"
],
"id": "https://mono.hyuki.net/actor",
"type": "Person",
"preferredUsername": "alice",
"inbox": "https://mono.hyuki.net/inbox",
"publicKey": {
"id": "https://mono.hyuki.net/actor#main-key",
"owner": "https://mono.hyuki.net/actor",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx4ewakZgFKKtfvCk/Yet\nyqNpYykroa/v/bkxXP5mnZon/roCn/6hJJPXnchHf6SMhlMTBbEjcIzxen6FiXtS\ndnrI/HmJYN7bVee6g2kRqlnN8+wB8qC5um0WGqUQqS6+gBKXaOxbhsN0SnKl2ofb\nS2sEn8AyakYizTHuvIdhZzEohO6fUcSDr/dalVlr97eHuAG/ARxyhhLbPOzAB5BG\nOo64OtKJd4Y8YvuvkiN11hlJ0NAAFqNJKIf4kpNUo2AImTfppqAHkPRP898UuprD\nfp8y1Nx1KDpOIA4EUId6MZ6QkT7HwYE8SH3xU/j58rBmD9a7pJc4e7I7kkmKwBAD\nfwIDAQAB\n-----END PUBLIC KEY-----\n"
}
}
{
"@context": "https://www.w3.org/ns/activitystreams",
"id": "https://mono.hyuki.net/create-hello-world",
"type": "Create",
"actor": "https://mono.hyuki.net/actor",
"object": {
"id": "https://mono.hyuki.net/hello-world",
"type": "Note",
"published": "2022-11-29T20:02:00+09:00",
"attributedTo": "https://mono.hyuki.net/actor",
"inReplyTo": "https://social.hyuki.net/@hyuki/109420962427229918",
"content": "<p>Hello world</p>",
"to": "https://www.w3.org/ns/activitystreams#Public"
}
}
require 'http'
require 'openssl'
require 'pp'
host = 'social.hyuki.net'
document = File.read('create-hello-world.json')
date = Time.now.utc.httpdate
keypair = OpenSSL::PKey::RSA.new(File.read('private.pem'))
body_digest = Base64.strict_encode64(OpenSSL::Digest::SHA256.new.digest(document))
digest = "sha-256=" + body_digest
signed_string = "(request-target): post /inbox\ndigest: #{digest}\nhost: #{host}\ndate: #{date}"
signature = Base64.strict_encode64(keypair.sign(OpenSSL::Digest::SHA256.new, signed_string))
header = 'keyId="https://mono.hyuki.net/actor",headers="(request-target) digest host date",signature="' + signature + '"'
result = HTTP.headers({
'Digest': digest,
'Host': host,
'Date': date,
'Signature': header
}).post("https://#{host}/inbox", body: document)
pp result
{
"subject": "acct:alice@mono.hyuki.net",
"links": [
{
"rel": "self",
"type": "application/activity+json",
"href": "https://mono.hyuki.net/actor"
}
]
}
@hyuki
Copy link
Author

hyuki commented Nov 29, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment