mirror of
https://git.exozy.me/a/fuwuqi.git
synced 2024-11-28 21:21:01 +00:00
Add latest working code
This commit is contained in:
parent
f463134952
commit
a2e773ebd1
16 changed files with 95 additions and 224 deletions
16
README.md
16
README.md
|
@ -2,11 +2,9 @@
|
|||
|
||||
Fuwuqi (fúwùqì or 服务器 means "server" in Chinese) is a useless C2S ActivityPub server for "extremely hardcore" ActivityPub enthusiasts. Craft your own exquisite WebFinger response! Customize your actor object exactly like you want! Hack and extend the 100-line Python server code!
|
||||
|
||||
If that sounds like a world of pain, close this webpage now... OK, got that out of the way. Time for some fun!
|
||||
|
||||
## Configuration
|
||||
|
||||
First, clone this repo on your server.
|
||||
First, clone this repo on your server and your client device.
|
||||
|
||||
Now, generate an RSA keypair on your client device:
|
||||
```bash
|
||||
|
@ -14,22 +12,24 @@ openssl genrsa -out private.pem 2048
|
|||
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
|
||||
```
|
||||
|
||||
Rename `users/test.jsonld` to your username and pop it open in your favorite text editor. You should change `0.exozy.me` to match your server's domain name, `test` to your username, and the `publicKeyPem` field to the public key you just generated.
|
||||
On the server, rename `users/test.jsonld` to your username and pop it open in your favorite text editor. You should change `0.exozy.me` to match your server's domain name, `test` to your username, and the `publicKeyPem` field to the public key you just generated.
|
||||
|
||||
Onward! Now open `.well-known/webfinger` in your editor, and modify it similarly.
|
||||
Onward! Now open `.well-known/webfinger` in your editor, and modify it similarly. Finally, open `server.py` and update the domain, obviously.
|
||||
|
||||
That wasn't so bad, was it? (sobbing sounds)
|
||||
|
||||
## Usage
|
||||
|
||||
Alright, time for the real deal. Start up `python main.py` on your server. If you want to customize the port or whatever, just change the source code.
|
||||
Alright, time for the real deal. Start up `python server.py` on your server. If you want to customize the port or whatever, just change the source code.
|
||||
|
||||
Now on your client device, open up your favorite C2S ActivityPub client... oh wait... there aren't any! Welp, you'll just have to settle for reading the [AP spec](https://www.w3.org/TR/activitypub/), writing some homemade JSON, and figuring out how `python client.py` works.
|
||||
Now on your client device, open up your favorite C2S ActivityPub client... oh wait... there aren't any! Welp, you'll just have to settle for reading the [AP spec](https://www.w3.org/TR/activitypub/), writing some homemade JSON, and figuring out how `python client.py` works. That's rough, buddy. You'll have to learn how to manually write `Note`s, manually accept follow requests, and more! You can find some examples in this repo. HTTP signatures are generated client-side, for no good reason other than it works. If you want to view your unread messages, just `curl` your inbox. Easy as that.
|
||||
|
||||
Like and subscribe and enjoy your new "extremely hardcore" ActivityPub server!!! 🎉😎🚀🙃🥳
|
||||
Enjoy your new "extremely hardcore" ActivityPub server!!! 🎉😎🚀🙃🥳
|
||||
|
||||
## Resources
|
||||
|
||||
- https://www.w3.org/TR/activitypub/
|
||||
- https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/
|
||||
- https://blog.joinmastodon.org/2018/07/how-to-make-friends-and-verify-requests/
|
||||
- https://docs.joinmastodon.org/spec/
|
||||
- https://socialhub.activitypub.rocks/t/python-mastodon-server-post-with-http-signature/2757
|
||||
|
|
12
accept.jsonld
Normal file
12
accept.jsonld
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Accept",
|
||||
"actor": "https://0.exozy.me/users/test.jsonld",
|
||||
"object": {
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://social.exozy.me/b363f127-6422-4566-b8f1-878aa33b0e1c",
|
||||
"type": "Follow",
|
||||
"actor": "https://social.exozy.me/users/a",
|
||||
"object": "https://0.exozy.me/users/test.jsonld"
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://0.exozy.me/users/test.outbox/hello-world2",
|
||||
"type": "Create",
|
||||
"actor": "https://0.exozy.me/users/test.jsonld",
|
||||
"object": {
|
||||
"id": "https://0.exozy.me/users/test.statuses/hello-world2",
|
||||
"type": "Note",
|
||||
"attributedTo": "https://0.exozy.me/users/test.jsonld",
|
||||
"inReplyTo": "https://social.exozy.me/@a/109707513227348721",
|
||||
"content": "Hello from fuwuqi! 2",
|
||||
"to": "https://www.w3.org/ns/activitystreams#Public"
|
||||
}
|
||||
}
|
11
client.py
11
client.py
|
@ -3,16 +3,17 @@ from cryptography.hazmat.primitives.asymmetric import padding
|
|||
from base64 import b64encode
|
||||
from email.utils import formatdate
|
||||
from requests import post
|
||||
from sys import argv
|
||||
|
||||
date = formatdate(usegmt=True)
|
||||
|
||||
with open('activity.jsonld', 'rb') as f:
|
||||
with open(argv[1], 'rb') as f:
|
||||
activity = f.read()
|
||||
|
||||
digester = hashes.Hash(hashes.SHA256())
|
||||
digester.update(activity)
|
||||
digest = b64encode(digester.finalize()).decode()
|
||||
message = f'(request-target): post /users/a/inbox\nhost: social.exozy.me\ndate: {date}\ndigest: SHA-256={digest}'
|
||||
message = f'date: {date}\ndigest: SHA-256={digest}'
|
||||
|
||||
with open('private.pem', 'rb') as f:
|
||||
privkey = serialization.load_pem_private_key(f.read(), None)
|
||||
|
@ -22,11 +23,9 @@ signature = b64encode(privkey.sign(
|
|||
padding.PKCS1v15(),
|
||||
hashes.SHA256()
|
||||
)).decode()
|
||||
header = f'keyId="https://0.exozy.me/users/test.jsonld#main-key",headers="(request-target) host date digest",signature="{signature}"'
|
||||
header = f'keyId="https://0.exozy.me/users/test.jsonld#main-key",headers="date digest",signature="{signature}"'
|
||||
|
||||
resp = post('http://localhost:4200/users/test.outbox', headers={
|
||||
'(request-target)': 'post /users/a/inbox',
|
||||
'Host': 'social.exozy.me',
|
||||
resp = post('https://0.exozy.me/users/test.outbox', headers={
|
||||
'Date': date,
|
||||
'Digest': f'SHA-256={digest}',
|
||||
'Signature': header,
|
||||
|
|
14
create.jsonld
Normal file
14
create.jsonld
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://0.exozy.me/users/test.outbox/hello-world2",
|
||||
"type": "Create",
|
||||
"actor": "https://0.exozy.me/users/test.jsonld",
|
||||
"object": {
|
||||
"id": "https://0.exozy.me/users/test.statuses/hello-world2",
|
||||
"type": "Note",
|
||||
"attributedTo": "https://0.exozy.me/users/test.jsonld",
|
||||
"inReplyTo": "https://social.exozy.me/@a/109707513227348721",
|
||||
"content": "Hello from fuwuqi! 2",
|
||||
"to": "https://www.w3.org/ns/activitystreams#Public"
|
||||
}
|
||||
}
|
7
follow.jsonld
Normal file
7
follow.jsonld
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://0.exozy.me/users/test.outbox/follow",
|
||||
"type": "Follow",
|
||||
"actor": "https://0.exozy.me/users/test.jsonld",
|
||||
"object": "https://social.exozy.me/users/a"
|
||||
}
|
28
private.pem
28
private.pem
|
@ -1,28 +0,0 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCc+fpJFDUlPCoU
|
||||
wUmrsGDExb/hX8dhKMc9u0ov/ePBc5OY4tTPjCvghvUjCIqmyC7PS7JfUuNt7epD
|
||||
OpoJm7mF2gaj64mxqfPrICgtjARN8gUiOpCvcWggZDFgkMQuSFI7yG1s4oqhGJUq
|
||||
yMPnHhBwSRmQsBp5/xZWHTywSoTd8Li4uY6VJq/JxswuZqH1R2GWMVxpVJDuqjH0
|
||||
cW8oXZ6LgGY3ZZ0wZnwk3bSdzQ7GhmkGkwCtGoXSSuMiSEDn/0K8hkRQs9AKFgvo
|
||||
ByYtLSP/JPETzZ4UwEG/J6DwgIxHNxsKg36FWe/dxv/v4HA12hCNmq3REb+GFulz
|
||||
xEq35g71AgMBAAECggEACqnGcS67ZMyfSn/4xP4QYhLRc89MSBLQ5KYeJirCd70l
|
||||
97riMY5i2qKARhbJ8wYNJqK3OqzIQIrqoLzQguTo/NPbI0kYNjvbvbXrqhsP8yAv
|
||||
bhb5W27s36ppWkHAjyj1gRRz2R4obu9bDqggT/Olso2cpsyiTSA2qwyFt1n7MueQ
|
||||
ag/lSYAAnjRfnxnGiBwy+wURxZH+Y9uBhb9E8MZ+W+sUc7l8RDUfYPN9d7NmxfdV
|
||||
x6x+jk+OLpYotj1v6aTfdwBCqpdt2ulMZexTCfjwAIU/XI2uhqaO1dzKI+Ssr/WY
|
||||
j8R7LXV94pI1ZXdwAJ/8aWGllAhj1hpnhacjnr7JAQKBgQDZPj5zJVk7TBoRxQVw
|
||||
VCfmGwPwAkfmGGqn9g41VlKtyG+JhWQ7aAs0gMFwq6QGDEBloAiXQw7z033ZSNTP
|
||||
p61QCePq6fLGEgdsfgjGotEbZ2XDZdB6fdzK+sKJLQtXr8FVrywyNmlX0bk8IRSs
|
||||
hnJXYco/VTLeDiQePO2bjm0UdQKBgQC4+0hR/se6AHgN9449ruHLSOCcC+Y6WQJ1
|
||||
BM+ydk7OnnfDmxzXQMbzri5kwDTBw+Y01avbRkpMZbHtWip9QWL96Kf8D6OUoX6d
|
||||
KJuheKsuJUKmXTPF8YyJiY+KXmx1yz7hu8X/SN955s4BAttIZGQvSIFpy+izzQWc
|
||||
FghcplvAgQKBgQCIkuIN37AOYFSPUU6PBMkkl11NWRG8bSM4Pq9GBuPpjvXX/f06
|
||||
f7lzo3J5E98FUlR1zzs3ZRgUX6RhorDvb1m81Mrtl3Bh51m1cjKwNhHB6aoHQo3j
|
||||
RBc3oJgGR0Q3Ny4TYRIm6yAk7ptGWwG1SLy/hKHyWOymvzsjq2gxgEPBNQKBgEyM
|
||||
6KvOBPdLVGNrS/jo01Yd7Z2GKxuAVEz61bzjys8ksylGmpPVob+cGGTnSa3aFP1O
|
||||
Y1VV7E9bUluIEcdN9NpgmovsKOTMRCpjcKxM1II/NyrDrTZANMmCHN3FH5tLpdUi
|
||||
sNhpXtoCksPGW9rEeNU8axnOIZmuwaCLWaCF07iBAoGAX494Slca2EnERTtcX9jq
|
||||
SM/srMTz+cm5zNA3npMj1eZ7zNglD5tZapc3f5OErrDdZz5wlmIo/eheQxb8TMQW
|
||||
F5goYHwpq6bghHlbAxK2I353s8Q8WriLTvAYouHfEqd14AS42OXD44CBwU0V5rRV
|
||||
MmlfOVBGqPVHLQK/tOebZIw=
|
||||
-----END PRIVATE KEY-----
|
|
@ -1,9 +0,0 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnPn6SRQ1JTwqFMFJq7Bg
|
||||
xMW/4V/HYSjHPbtKL/3jwXOTmOLUz4wr4Ib1IwiKpsguz0uyX1Ljbe3qQzqaCZu5
|
||||
hdoGo+uJsanz6yAoLYwETfIFIjqQr3FoIGQxYJDELkhSO8htbOKKoRiVKsjD5x4Q
|
||||
cEkZkLAaef8WVh08sEqE3fC4uLmOlSavycbMLmah9UdhljFcaVSQ7qox9HFvKF2e
|
||||
i4BmN2WdMGZ8JN20nc0OxoZpBpMArRqF0krjIkhA5/9CvIZEULPQChYL6AcmLS0j
|
||||
/yTxE82eFMBBvyeg8ICMRzcbCoN+hVnv3cb/7+BwNdoQjZqt0RG/hhbpc8RKt+YO
|
||||
9QIDAQAB
|
||||
-----END PUBLIC KEY-----
|
61
server.py
61
server.py
|
@ -9,6 +9,9 @@ from os.path import isfile
|
|||
from urllib.parse import quote_plus
|
||||
|
||||
|
||||
domain = 'https://0.exozy.me'
|
||||
|
||||
|
||||
def collection_append(file, item):
|
||||
with open(file) as f:
|
||||
collection = load(f)
|
||||
|
@ -18,6 +21,20 @@ def collection_append(file, item):
|
|||
dump(collection, f)
|
||||
|
||||
|
||||
def iri_to_actor(iri):
|
||||
if domain in iri:
|
||||
name = search(f'^{domain}/users/(.*?)#main-key$', iri).group(1)
|
||||
actorfile = f'users/{name}'
|
||||
else:
|
||||
actorfile = f'users/{quote_plus(iri)}'
|
||||
if not isfile(actorfile):
|
||||
with open(actorfile, 'w') as f:
|
||||
resp = get(iri, headers={'Accept': 'application/activity+json'})
|
||||
f.write(resp.text)
|
||||
with open(actorfile) as f:
|
||||
return load(f)
|
||||
|
||||
|
||||
class fuwuqi(SimpleHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
body = self.rfile.read(int(self.headers['Content-Length']))
|
||||
|
@ -26,21 +43,22 @@ class fuwuqi(SimpleHTTPRequestHandler):
|
|||
print(self.headers)
|
||||
print(self.path)
|
||||
|
||||
username = search('^/users/(.*)\.(in|out)box$', self.path).group(1)
|
||||
|
||||
# Get actor public key
|
||||
keyid = search('keyId="(.*?)"', self.headers['Signature']).group(1)
|
||||
actorfile = f'users/{quote_plus(keyid)}'
|
||||
if not isfile(actorfile):
|
||||
with open(actorfile, 'w') as f:
|
||||
f.write(get(keyid).text)
|
||||
with open(actorfile) as f:
|
||||
pubkeypem = load(f)['publicKey']['publicKeyPem'].encode('utf8')
|
||||
pubkey = serialization.load_pem_public_key(pubkeypem, None)
|
||||
actor = iri_to_actor(keyid)
|
||||
pubkeypem = actor['publicKey']['publicKeyPem'].encode('utf8')
|
||||
pubkey = serialization.load_pem_public_key(pubkeypem, None)
|
||||
|
||||
# Assemble headers
|
||||
headers = search('headers="(.*?)"', self.headers['Signature']).group(1)
|
||||
message = ''
|
||||
for header in headers.split():
|
||||
headerval = self.headers[header]
|
||||
if header == '(request-target)':
|
||||
headerval = f'post {self.path}'
|
||||
else:
|
||||
headerval = self.headers[header]
|
||||
message += f'{header}: {headerval}\n'
|
||||
|
||||
# Verify HTTP signature
|
||||
|
@ -61,27 +79,34 @@ class fuwuqi(SimpleHTTPRequestHandler):
|
|||
self.send_response(401)
|
||||
return
|
||||
|
||||
username = search('^/users/(.*)\.(in|out)box$', self.path).group(1)
|
||||
if self.path.endswith('inbox'):
|
||||
# S2S
|
||||
id = activity['id'].split('/')[-1]
|
||||
with open(f'users/{username}/{id}', 'w') as f:
|
||||
dump(activity, f)
|
||||
collection_append(f'users/{username}.inbox', activity)
|
||||
elif self.path.endswith('outbox'):
|
||||
# C2S
|
||||
collection_append(f'users/{username}.outbox', activity)
|
||||
|
||||
if activity['type'] == 'Create':
|
||||
id = activity['id'].split('/')[-1]
|
||||
with open(f'users/{username}.statuses/{id}', 'w') as f:
|
||||
dump(activity['object'], f)
|
||||
print(self.headers)
|
||||
print(body)
|
||||
resp = post('https://social.exozy.me/users/a/inbox', headers=self.headers, data=body)
|
||||
print(resp)
|
||||
print(resp.text)
|
||||
# Send to followers
|
||||
with open(f'users/{username}.following') as f:
|
||||
for followed in load(f)['orderedItems']:
|
||||
actor = iri_to_actor(followed)
|
||||
self.headers['Host'] = followed.split('/')[2]
|
||||
resp = post(actor['inbox'], headers=self.headers, data=body)
|
||||
print(resp)
|
||||
print(resp.text)
|
||||
elif activity['type'] == 'Follow':
|
||||
object = iri_to_actor(activity['object'])
|
||||
self.headers['Host'] = activity['object'].split('/')[2]
|
||||
resp = post(object['inbox'], headers=self.headers, data=body)
|
||||
print(resp)
|
||||
print(resp.text)
|
||||
collection_append(f'users/{username}.following', activity['object'])
|
||||
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
||||
|
||||
ThreadingHTTPServer(('localhost', 4200), fuwuqi).serve_forever()
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1"
|
||||
],
|
||||
"id": "https://0.exozy.me/users/test.jsonld",
|
||||
"type": "Person",
|
||||
"preferredUsername": "test",
|
||||
"name": "Billiam Wender",
|
||||
"inbox": "https://0.exozy.me/users/test.inbox",
|
||||
"outbox": "https://0.exozy.me/users/test.outbox",
|
||||
"followers": "https://0.exozy.me/users/test.followers",
|
||||
"following": "https://0.exozy.me/users/test.following",
|
||||
"icon": {
|
||||
"type": "Image",
|
||||
"mediaType": "image/png",
|
||||
"url": "https://0.exozy.me/users/test.png"
|
||||
},
|
||||
"publicKey": {
|
||||
"id": "https://0.exozy.me/users/test.jsonld#main-key",
|
||||
"owner": "https://0.exozy.me/users/test.jsonld",
|
||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnPn6SRQ1JTwqFMFJq7Bg\nxMW/4V/HYSjHPbtKL/3jwXOTmOLUz4wr4Ib1IwiKpsguz0uyX1Ljbe3qQzqaCZu5\nhdoGo+uJsanz6yAoLYwETfIFIjqQr3FoIGQxYJDELkhSO8htbOKKoRiVKsjD5x4Q\ncEkZkLAaef8WVh08sEqE3fC4uLmOlSavycbMLmah9UdhljFcaVSQ7qox9HFvKF2e\ni4BmN2WdMGZ8JN20nc0OxoZpBpMArRqF0krjIkhA5/9CvIZEULPQChYL6AcmLS0j\n/yTxE82eFMBBvyeg8ICMRzcbCoN+hVnv3cb/7+BwNdoQjZqt0RG/hhbpc8RKt+YO\n9QIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||
}
|
||||
}
|
1
users/https%3A%2F%2Fsocial.exozy.me%2Fusers%2Fa
Normal file
1
users/https%3A%2F%2Fsocial.exozy.me%2Fusers%2Fa
Normal file
|
@ -0,0 +1 @@
|
|||
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","toot":"http://joinmastodon.org/ns#","featured":{"@id":"toot:featured","@type":"@id"},"featuredTags":{"@id":"toot:featuredTags","@type":"@id"},"alsoKnownAs":{"@id":"as:alsoKnownAs","@type":"@id"},"movedTo":{"@id":"as:movedTo","@type":"@id"},"schema":"http://schema.org#","PropertyValue":"schema:PropertyValue","value":"schema:value","discoverable":"toot:discoverable","Device":"toot:Device","Ed25519Signature":"toot:Ed25519Signature","Ed25519Key":"toot:Ed25519Key","Curve25519Key":"toot:Curve25519Key","EncryptedMessage":"toot:EncryptedMessage","publicKeyBase64":"toot:publicKeyBase64","deviceId":"toot:deviceId","claim":{"@type":"@id","@id":"toot:claim"},"fingerprintKey":{"@type":"@id","@id":"toot:fingerprintKey"},"identityKey":{"@type":"@id","@id":"toot:identityKey"},"devices":{"@type":"@id","@id":"toot:devices"},"messageFranking":"toot:messageFranking","messageType":"toot:messageType","cipherText":"toot:cipherText","suspended":"toot:suspended","focalPoint":{"@container":"@list","@id":"toot:focalPoint"}}],"id":"https://social.exozy.me/users/a","type":"Person","following":"https://social.exozy.me/users/a/following","followers":"https://social.exozy.me/users/a/followers","inbox":"https://social.exozy.me/users/a/inbox","outbox":"https://social.exozy.me/users/a/outbox","featured":"https://social.exozy.me/users/a/collections/featured","featuredTags":"https://social.exozy.me/users/a/collections/tags","preferredUsername":"a","name":"","summary":"\u003cp\u003e(+ (My username) (Ordinal n) (! off) (Element 39) (Wide area network) (Prefix for billion))\u003c/p\u003e\u003cp\u003eBoosts interesting things and sometimes transcribes special patterns generated by my biological neural networks into UTF-8 encoded strings.\u003c/p\u003e\u003cp\u003eCurrently oscillating between hacking ActivityPub federation into Forgejo/Gitea, pondering about the ForgeFed spec, wasting time, and sleeping (with 1/3 probability!).\u003c/p\u003e\u003cp\u003eWill happily answer any questions about forge federation.\u003c/p\u003e","url":"https://social.exozy.me/@a","manuallyApprovesFollowers":false,"discoverable":true,"published":"2022-09-07T00:00:00Z","devices":"https://social.exozy.me/users/a/collections/devices","alsoKnownAs":["https://social.exozy.me/users/ta180m"],"publicKey":{"id":"https://social.exozy.me/users/a#main-key","owner":"https://social.exozy.me/users/a","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxrHXYuP4RKjac7h0woPA\nmBeETGogZ2IPUbDhA4DliUUHbUGYIK3XeIA4iywpnTbuHxE7L2PqMYNvfYMjQ+HS\nlJfSgYLB5mFxvzNcKqaUfAZazx3RzHLxftxt4DxWhEtS+vVd4RsxU2uvCOU8nN7o\nvk40GaYrgAms/7sapAjqbn6ngclVtVOBm0QhCG9cfg4QZoIIp98wpj/7kWZHxl8Y\nKJKN4G66FP+WAPIiLO/EmBqB9jcTQM2UMIGol4+616zFbmrow4KFCxZhiap+doP2\n8SjZSlA4Fhlk4qtHvQRjPlx2u/gvyUc7gQoa3PTb64rdDw1ahGvLTQKB6uVAXpDN\n2wIDAQAB\n-----END PUBLIC KEY-----\n"},"tag":[],"attachment":[{"type":"PropertyValue","name":"Website","value":"\u003ca href=\"https://a.exozy.me\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003ea.exozy.me\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"},{"type":"PropertyValue","name":"Code","value":"\u003ca href=\"https://git.exozy.me/a\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egit.exozy.me/a\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"},{"type":"PropertyValue","name":"exozyme","value":"\u003ca href=\"https://exozy.me\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003eexozy.me\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e"},{"type":"PropertyValue","name":"Also me","value":"\u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.mit.edu/@xy\" class=\"u-url mention\"\u003e@\u003cspan\u003exy@mastodon.mit.edu\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e"}],"endpoints":{"sharedInbox":"https://social.exozy.me/inbox"},"icon":{"type":"Image","mediaType":"image/gif","url":"https://social.exozy.me/system/accounts/avatars/108/958/469/936/988/491/original/c16834437aa85a2a.gif"},"image":{"type":"Image","mediaType":"image/jpeg","url":"https://social.exozy.me/system/accounts/headers/108/958/469/936/988/491/original/521f64b30b55553d.jpg"}}
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "OrderedCollection",
|
||||
"totalItems": 0,
|
||||
"orderedItems": [],
|
||||
}
|
||||
{"@context": "https://www.w3.org/ns/activitystreams", "type": "OrderedCollection", "totalItems": 3, "orderedItems": ["https://social.exozy.me/users/a", "https://social.exozy.me/users/a", "https://social.exozy.me/users/a"]}
|
|
@ -1,6 +1 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "OrderedCollection",
|
||||
"totalItems": 0,
|
||||
"orderedItems": [],
|
||||
}
|
||||
{"@context": "https://www.w3.org/ns/activitystreams", "type": "OrderedCollection", "totalItems": 2, "orderedItems": ["https://social.exozy.me/users/a", "https://social.exozy.me/users/a"]}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue