mirror of
https://git.exozy.me/a/fuwuqi.git
synced 2024-11-22 08:51:10 +00:00
Merge branch 'main' of git.exozy.me:a/fuwuqi
This commit is contained in:
commit
1e65c7b7ef
6 changed files with 56 additions and 28 deletions
|
@ -37,9 +37,7 @@ Enjoy your new "extremely hardcore" ActivityPub server!!! 🎉😎🚀🙃🥳
|
|||
|
||||
Since Fuwuqi's code is super duper easy to read and extend, the following features are left as an exercise to the reader:
|
||||
- Multi-user support (hint: dynamically generate `.well-known/webfinger` instead of serving a static file)
|
||||
- Liking posts (hint: the implementation is very similar to `Follow`)
|
||||
- Announcing posts, AKA boosting
|
||||
- Unfollowing, unliking, etc (hint: implement `collection_remove`)
|
||||
- Deleting posts
|
||||
- JSON-LD (hint: don't do it, your brain will thank you)
|
||||
- Lots of pain
|
||||
|
||||
|
|
|
@ -5,11 +5,10 @@ from email.utils import formatdate
|
|||
from requests import post
|
||||
from sys import argv
|
||||
|
||||
date = formatdate(usegmt=True)
|
||||
|
||||
with open(argv[1], 'rb') as f:
|
||||
activity = f.read()
|
||||
|
||||
date = formatdate(usegmt=True)
|
||||
digester = hashes.Hash(hashes.SHA256())
|
||||
digester.update(activity)
|
||||
digest = b64encode(digester.finalize()).decode()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"id": "https://0.exozy.me/users/test.outbox/follow",
|
||||
"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/guest",
|
||||
|
|
72
server.py
72
server.py
|
@ -15,26 +15,43 @@ domain = 'https://0.exozy.me'
|
|||
def collection_append(file, item):
|
||||
with open(file) as f:
|
||||
collection = load(f)
|
||||
collection['totalItems'] += 1
|
||||
collection['orderedItems'].append(item)
|
||||
collection['totalItems'] += 1
|
||||
with open(file, 'w') as f:
|
||||
dump(collection, f)
|
||||
|
||||
|
||||
def collection_pop(file, item):
|
||||
with open(file) as f:
|
||||
collection = load(f)
|
||||
collection['orderedItems'].pop(item)
|
||||
collection['totalItems'] -= 1
|
||||
with open(file, 'w') as f:
|
||||
dump(collection, f)
|
||||
|
||||
|
||||
def iri_to_actor(iri):
|
||||
if domain in iri:
|
||||
name = search(f'^{domain}/users/(.*?)#main-key$', iri).group(1)
|
||||
name = search(f'^{domain}/users/(.*?)$', iri.removesuffix('#main-key')).group(1)
|
||||
actorfile = f'users/{name}'
|
||||
else:
|
||||
actorfile = f'users/{quote_plus(iri)}'
|
||||
actorfile = f'users/{quote_plus(iri.removesuffix("#main-key"))}'
|
||||
if not isfile(actorfile):
|
||||
with open(actorfile, 'w') as f:
|
||||
resp = get(iri.removesuffix('#main-key'), headers={'Accept': 'application/activity+json'})
|
||||
resp = get(iri, headers={'Accept': 'application/activity+json'})
|
||||
f.write(resp.text)
|
||||
with open(actorfile) as f:
|
||||
return load(f)
|
||||
|
||||
|
||||
def send(to, headers, body):
|
||||
actor = iri_to_actor(to)
|
||||
headers['Host'] = to.split('/')[2]
|
||||
resp = post(actor['inbox'], headers=headers, data=body)
|
||||
print(resp)
|
||||
print(resp.text)
|
||||
|
||||
|
||||
class fuwuqi(SimpleHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
body = self.rfile.read(int(self.headers['Content-Length']))
|
||||
|
@ -72,10 +89,10 @@ class fuwuqi(SimpleHTTPRequestHandler):
|
|||
|
||||
# Make sure activity doer matches HTTP signature
|
||||
actor = keyid.removesuffix('#main-key')
|
||||
if 'actor' in activity and activity['actor'] != actor:
|
||||
self.send_response(401)
|
||||
return
|
||||
if 'attributedTo' in activity and activity['attributedTo'] != actor:
|
||||
if ('actor' in activity and activity['actor'] != actor) or
|
||||
('attributedTo' in activity and activity['attributedTo'] != actor) or
|
||||
('actor' in activity['object'] and activity['object']['actor'] != actor) or
|
||||
('attributedTo' in activity['object'] and activity['object']['attributedTo'] != actor)
|
||||
self.send_response(401)
|
||||
return
|
||||
|
||||
|
@ -85,25 +102,36 @@ class fuwuqi(SimpleHTTPRequestHandler):
|
|||
elif self.path.endswith('outbox'):
|
||||
# C2S
|
||||
collection_append(f'users/{username}.outbox', activity)
|
||||
# Clients responsible for addressing activity
|
||||
for to in activity['to']:
|
||||
if 'followers' in to or to 'https://www.w3.org/ns/activitystreams#Public':
|
||||
with open(f'users/{username}.followers') as f:
|
||||
for follower in load(f)['orderedItems']:
|
||||
send(follower, self.headers, body)
|
||||
else:
|
||||
send(to, self.headers, body)
|
||||
# Process activity
|
||||
if activity['type'] == 'Create':
|
||||
id = activity['id'].split('/')[-1]
|
||||
# Post
|
||||
id = activity['object']['id'].split('/')[-1]
|
||||
with open(f'users/{username}.statuses/{id}', 'w') as f:
|
||||
dump(activity['object'], f)
|
||||
# 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'] == 'Accept':
|
||||
# Accept follow request
|
||||
collection_append(f'users/{username}.followers', activity['object']['actor'])
|
||||
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)
|
||||
# Follow request
|
||||
collection_append(f'users/{username}.following', activity['object'])
|
||||
elif activity['type'] == 'Like':
|
||||
# Like post
|
||||
collection_append(f'users/{username}.liked', activity['object'])
|
||||
elif activity['type'] == 'Undo':
|
||||
if activity['object']['type'] == 'Follow':
|
||||
# Unfollow request
|
||||
collection_remove(f'users/{username}.following', activity['object']['object'])
|
||||
elif activity['object']['type'] == 'Like':
|
||||
# Unlike post
|
||||
collection_remove(f'users/{username}.liked', activity['object']['object'])
|
||||
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
|
|
|
@ -7,10 +7,12 @@
|
|||
"type": "Person",
|
||||
"preferredUsername": "test",
|
||||
"name": "Billiam Wender",
|
||||
"summary": "idk",
|
||||
"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",
|
||||
"liked": "https://0.exozy.me/users/test.liked",
|
||||
"icon": {
|
||||
"type": "Image",
|
||||
"mediaType": "image/png",
|
||||
|
|
1
users/test.liked
Normal file
1
users/test.liked
Normal file
|
@ -0,0 +1 @@
|
|||
{"@context": "https://www.w3.org/ns/activitystreams", "type": "OrderedCollection", "totalItems": 0, "orderedItems": []}
|
Loading…
Reference in a new issue