def dbm_denormalize_activity(asobj, db): as_json = asobj.json() def maybe_denormalize(key): val = as_json.get(key) # If there's no specific val, # it's not a string, or it's not in the database, # just leave it! if val is None or not isinstance(val, str) or val not in db: return # Otherwise, looks like that value *is* in the database... hey! # Let's pull it out and set it as the key. as_json[key] = db[val] maybe_denormalize("actor") maybe_denormalize("object") maybe_denormalize("target") return core.ASObj(as_json, asobj.env)
def maybe_normalize(key): val = as_json.get(key) # Skip if not a dictionary with a "@type" if not isinstance(val, dict) or not "@type" in val: return val_asobj = core.ASObj(val, asobj.env) # yup, time to normalize if asobj.env.is_astype(val_asobj, vocab.Object, inherit=True): # If there's no id, then okay, don't normalize if val_asobj.id is None: return if val_asobj.id not in db: # save to the database asobj.env.asobj_run_method(val_asobj, dbm_save_method, db) # and set the key to be the .id as_json[key] = val_asobj.id
def dbm_fetch(id, db, env): return core.ASObj(db[id], env)
def fetch_asobj(self, env): return core.ASObj(self[id], env)
def as_asobj(obj): return core.ASObj(obj)
def feed_post(handle): """ adds objects that it receives to mongodb and sends them along to appropriate Actor inboxes """ r = core.ASObj(request.get_json(), vocab.BasicEnv) u = find_user(handle) # if it's a note it turns it into a Create object if 'Note' in r.types: obj = r.get_json() r = vocab.Create(obj['@id'] + '/activity', actor=u['@id'], published=obj['published'], to=obj['to'], bto=obj['bto'], cc=obj['cc'], bcc=obj['bcc'], audience=obj['audience'], obj=obj) if 'Create' in r.types: if r['object']['@type'] != 'Note': print(str(r)) print('not a note') return Response(status=403) mongo.db.users.update({'acct': u['acct']}, {'$inc': { 'metrics.post_count': 1 }}) elif 'Update' in r.types: return Response(status=501) elif 'Delete' in r.types: return Response(status=501) elif 'Follow' in r.types: return Response(status=501) elif 'Accept' in r.types: return Response(status=501) elif 'Reject' in r.types: return Response(status=501) elif 'Add' in r.types: return Response(status=501) elif 'Remove' in r.types: return Response(status=501) elif 'Like' in r.types: if u['acct'] not in mongo.db.posts.find({'@id': r['object']['@id']})['likes']: mongo.db.posts.update({'@id': r['object']['@id']}, {'$push': { 'likes': u['acct'] }}) elif 'Announce' in r.types: return Response(status=501) elif 'Undo' in r.types: return Response(status=501) recipients = [] r = r.json() for group in ['to', 'bto', 'cc', 'bcc', 'audience']: addresses = r.get(group, []) recipients.extend(addresses) for address in addresses: requests.post(address, json=r, headers=content_headers(u)) try: mongo.db.posts.insert_one(r) return Response(status=201) except: return Response(status=500)
def inbox(handle): if request.method == 'GET': """ think of this as the "Home" feed on mastodon. returns all Objects addressed to the user. this should require authentication """ u = find_user(handle) items = list( mongo.db.posts.find({ 'to': u['@id'] }, { '_id': False }).sort('published', -1)) resp = vocab.OrderedCollection(u['inbox'], totalItems=len(items), orderedItems=items) return Response(resp, headers=content_headers(find_user(handle))) if request.method == 'POST': """ deduplicates requests, and adds them to the database. in some cases (e.g. Follow requests) it automatically responds, pending fuller API and UI implementation """ print('inbox post') u = find_user(handle) r = core.ASObj(request.get_json()) if 'Create' in r.types: # this needs more stuff, like creating a user if necessary if mongo.db.posts.find({'@id': r['@id']}) is not None: try: r.update(dict(origin=request.environ['HTTP_ORIGIN'])) mongo.db.posts.insert_one(r) return Response(status=201) except: return Response(status=500) elif 'Update' in r.types: if 'Actor' in r.types: time = get_time() r['updated'] = time try: mongo.db.users.find_one_and_replace({'@id': r['@id']}, r.json(), {'upsert': True}) return Response(status=201) except: return Response(status=500) elif ('Object' or 'Activity') in r.types: try: mongo.db.posts.find_one_and_replace({'@id': r['@id']}, r.json(), {'upsert': True}) return Response(status=201) except: return Response(status=500) elif 'Delete' in r.types: time = get_time() tombstone = vocab.Tombstone(r['@id'], published=r['published'], updated=time, deleted=time) if 'Actor' in r.types: try: mongo.db.users.find_one_and_replace({'@id': r['@id']}, tombstone.json()) return Response(status=201) except: return Response(status=500) elif ('Object' or 'Activity') in r.types: try: mongo.db.posts.find_one_and_replace({'@id': r['@id']}, tombstone.json()) return Response(status=201) except: return Response(status=500) return Response(status=501) elif 'Follow' in r.types: if u.get('followers_coll'): if u['followers_coll'].get('actor'): return 400 mongo.db.users.update_one( {'id': u['@id']}, {'$push': { 'followers_coll': r['actor'] }}, upsert=True) to = requests.get(r['actor'], headers=accept_headers(u)).json()['inbox'] accept = vocab.accept(to=to, object=r.get_json()).json() headers = content_headers(u) try: requests.post(to, json=accept, headers=headers) return Response(status=201) except: return Response(status=500) elif 'Accept' in r.types: print('received Accept') try: mongo.db.users.update_one( {'id': u['@id']}, {'$push': { 'following_coll': r['object']['actor'] }}, upsert=True) return Response(status=201) except: return Response(status=501) elif 'Reject' in r.types: return Response(status=200) elif 'Add' in r.types: return Response(status=501) elif 'Remove' in r.types: return Response(status=501) elif 'Like' in r.types: try: mongo.db.posts.update_one( {'id': r['object']}, {'$push': { 'object.liked_coll': r['actor'] }}, upsert=True) return Response(status=201) except: return Response(status=500) elif 'Announce' in r.types: try: mongo.db.posts.update_one( {'id': r['object']}, {'$push': { 'object.shared_coll': r['actor'] }}, upsert=True) return Response(status=201) except: return Response(status=500) elif 'Undo' in r.types: # this requires maybe a lot of stuff for implementing? # i'll think about it later return Response(status=501) else: print('other type') print(r) abort(400)
def feed(handle): if request.method == 'GET': """ per AP spec, returns a reverse-chronological OrderedCollection of items in the outbox, pending privacy settings """ print('feed get') u = find_user(handle) items = list( mongo.db.posts.find({ 'object.attributedTo': u['@id'] }, { '_id': False }).sort('published', -1)) resp = vocab.OrderedCollection(u['outbox'], totalItems=len(items), orderedItems=items) return Response(json.dumps(resp.json()), headers=content_headers(u)) if request.method == 'POST': """ adds objects that it receives to mongodb and sends them along to appropriate Actor inboxes """ r = core.ASObj(request.get_json(), vocab.BasicEnv) u = find_user(handle) # if it's a note it turns it into a Create object if 'Note' in r.types: obj = r.get_json() r = vocab.Create(obj['@id'] + '/activity', actor=u['@id'], published=obj['published'], to=obj['to'], bto=obj['bto'], cc=obj['cc'], bcc=obj['bcc'], audience=obj['audience'], obj=obj) if 'Create' in r.types: mongo.db.users.update({'acct': u['acct']}, {'$inc': { 'metrics.post_count': 1 }}) elif 'Update' in r.types: return Response(status=501) elif 'Delete' in r.types: return Response(status=501) elif 'Follow' in r.types: return Response(status=501) elif 'Accept' in r.types: return Response(status=501) elif 'Reject' in r.types: return Response(status=501) elif 'Add' in r.types: return Response(status=501) elif 'Remove' in r.types: return Response(status=501) elif 'Like' in r.types: if u['acct'] not in mongo.db.posts.find( {'@id': r['object']['@id']})['likes']: try: mongo.db.posts.update({'@id': r['object']['@id']}, {'$push': { 'likes': u['acct'] }}) return Response(status=201) except: return Response(status=500) elif 'Announce' in r.types: return Response(status=501) elif 'Undo' in r.types: return Response(status=501) recipients = [] r = r.json() for group in ['to', 'bto', 'cc', 'bcc', 'audience']: addresses = r.get(group, []) recipients.extend(addresses) for address in addresses: requests.post(address, json=r, headers=content_headers(u)) try: mongo.db.posts.insert_one(r) return Response(status=201) except: return Response(status=500)
"object": { "@type": "Note", "@id": "http://tsyesika.co.uk/chat/sup-yo/", "content": "Up for some root beer floats?" } } ROOT_BEER_NOTE_MIXED_ASOBJ = { "@type": "Create", "@id": "http://tsyesika.co.uk/act/foo-id-here/", "actor": core.ASObj({ "@type": "Person", "@id": "http://tsyesika.co.uk/", "displayName": "Jessica Tallon" }), "to": [ "acct:[email protected]", "acct:[email protected]", "acct:[email protected]" ], "object": core.ASObj({ "@type": "Note", "@id": "http://tsyesika.co.uk/chat/sup-yo/", "content": "Up for some root beer floats?" }) } ROOT_BEER_NOTE_VOCAB = vocab.Create(
def mongo_fetch(id, db, env): return core.ASObj(db[id], env)