Mastodonの検索エリアにURLを突っ込んだ際にアカウント化(?)させるにはどういう実装が必要なのか、気になって調べて実装してみた。
結局
- ActivityPub対応
- WebFinger対応
- HTTPS化
で受け入れられる。
参考
Personを返すActivityPubの実装
GETアクセスした際に以下のJSONを返す。
{
'@context': 'https://www.w3.org/ns/activitystreams',
'type': 'Person',
'id': 'https://example.com/test', # Fediverseで一意
'name': 'test', # 表示名
'preferredUsername': 'test', # ユーザID
'summary': 'my simple activitypub', # 概要
'inbox': 'https://example.com/inbox', # このユーザへの宛先
'outbox': 'https://example.com/outbox', # このユーザの発信元
'url': 'https://example.com/test', # プロフィールページのURL
'icon': {
'type': 'Image',
'mediaType': 'image/png', # mime type
'url': 'https://example.com/icon.ong' # アイコン画像のURL
}
}
Flaskで実装する例。
$ pip install flask
api.py
import json
from flask import Flask, Response
app = Flask(__name__)
@app.route('/')
def person():
response = {
...
}
return Response(json.dumps(response), headers={'Content-Type': 'application/activity+json'})
if __name__ == "__main__":
app.run()
WebFingerの実装
あまり記事で見かけないが、MastodonはWebFingerにも対応させる必要がある(インターネット界の暗黙的なルール?)。
ことの始まりとして/.well-known/host-meta
というパスへのアクセスに対して
<?xml version="1.0"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
<Link rel="lrdd" type="application/xrd+xml" template="https://example.com/.well-known/webfinger?resource={uri}" />
</XRD>
のようなXMLを返す。これを受けてMasotodnはtemplate
のURLにアクセスしてくるので、
{
'subject': 'acct:test@example.com',
'links': [
{
'rel': 'self',
'type': 'application/activity+json',
'href': 'https://example.com/test'
},
]
}
を返すようにする。{uri}
には求めているユーザのsubject
にあたる文字列が入っている。ユーザを複数扱う場合にはこれを参照して返すXMLを生成する。
Flaskで実装する例(1ユーザのみ版)。
webfinger.py
import json
from xml.dom.minidom import parseString
from flask import Flask, jsonify, Response
from .app import app
@app.route('/.well-known/host-meta')
def webfinger_host_meta():
xml_str = "<?xml version=\"1.0\"?>\
<XRD xmlns=\"http://docs.oasis-open.org/ns/xri/xrd-1.0\">\
<Link rel=\"lrdd\" type=\"application/xrd+xml\" template=\"https://example.com/.well-known/webfinger?resource={uri}\"/>\
</XRD>"
dom = parseString(xml_str)
return Response(dom.toprettyxml(), headers={'Content-Type': 'application/xml'})
@app.route('/.well-known/webfinger')
def webfinger_resource():
response = {
'subject': 'acct:test@example.com',
'links': [
{
'rel': 'self',
'type': 'application/activity+json',
'href': 'https://example.com/test'
},
]
}
return jsonify(response)
api.pyから読み込む。
api.py
...
import webfinger
if __name__ == "__main__":
app.run()
Flaskをとりあえず動かしてみる。
$ python api.py
本番サーバでHTTPS化
本番サーバではuWSGIを使う。
$ pip install uwsgi
設定ファイルを作る。
app.ini
[uwsgi]
# 初期起動ファイル
module = api
# Flask(__name__) 実体先
callable = app
master = true
processes = 1
socket = /tmp/uwsgi.sock
chmod-socket = 666
vacuum = true
die-on-term = true
次のコマンドで本番サーバが立ち上がる。
uwsgi --ini flask.ini
これをnginxでHTTPS化して公開する。
flask.conf
server {
listen 80;
listen [::]:80;
server_name example.com;
return 301 https://$host$request_uri;
}
server {
listen 443;
server_name example.com;
ssl_protocols TLSv1.2;
ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
include uwsgi_params;
uwsgi_pass unix:///tmp/uwsgi.sock;
}
}
https://example.com/test
をMastodonの検索エリアに突っ込むことでアカウントになって出てくると思う。
bot化する上で必要な事
- Noteを返すActivityPubの実装
- フォロワーの管理
- Activity(発言とか)をフォロワーのINBOXへPOST