Initial AP objects implementation

This commit is contained in:
2024-02-21 11:07:26 -03:00
parent a0b15f6526
commit f9dedca988
5 changed files with 167 additions and 1 deletions

View File

@@ -0,0 +1,50 @@
module ap.activity_stream;
import std.json;
import util;
/++
* Basic ActivityStream Object
* https://www.w3.org/TR/activitypub/#obj
+/
class ASObject {
string context; /// Must be activitystream context
string id; /// AP requirement for unique identifier
string type; /// AP requirement for type of object
JSONValue raw;
/++
+ Constructs the object according to json source
+ Params:
+ json = the source json
+ Returns: Object
+/
this(JSONValue json) {
with (this) {
const(JSONValue)* ascontext = "@context" in json;
if (ascontext == null)
throw new Exception("No context in ActivityStream Object");
switch ((*ascontext).type) {
case JSONType.ARRAY:
context = (*ascontext)[0].str;
break;
case JSONType.STRING:
context = (*ascontext).str;
break;
default:
throw new Exception("Invalid @context type for ActivityStream Object");
}
id = json.requiredKey!"id".str;
type = json.requiredKey!"type".str;
raw = json;
}
}
}

77
source/ap/actor.d Normal file
View File

@@ -0,0 +1,77 @@
module ap.actor;
import std.json;
import ap.activity_stream;
import util;
/++
* Represents an ActivityPub Actor
* https://www.w3.org/TR/activitypub/#actors
+/
class Actor : ASObject {
/* Required fields */
string inbox; /// Link to messages received by this actor. Required
string outbox; /// Link to messages produced by this actor. Required
string following; /// Link to a collection of the actors that this actor is following. Required
string followers; /// Link to a collection of the actors that follow this actor. Required
/* Optional fields */
string liked; /// Link to a collection of objects this actor has liked
string streams; /// Supplementary list of Collections of interest
string preferredUsername; /// A short username for referencing this actor
ActorEndpoints endpoints; /// Useful endpoints for this actor
JSONValue raw;
/++
+ Creates an Actor object based on its json representation
+ Params:
+ json = ActivityPub actor JSON representation
+/
this(JSONValue json) {
super(json);
with (this) {
inbox = json.requiredKey!"inbox".str;
outbox = json.requiredKey!"outbox".str;
following = json.requiredKey!"following".str;
followers = json.requiredKey!"followers".str;
liked = json.checkKey!string("liked");
streams = json.checkKey!string("streams");
preferredUsername = json.checkKey!string("preferredUsername");
if (JSONValue* j = "endpoints" in json)
endpoints = ActorEndpoints.fromJson(*j);
else
endpoints = ActorEndpoints();
raw = json;
}
}
}
struct ActorEndpoints {
string proxyUrl;
string oauthAuthorizationEndpoint;
string oauthTokenEndpoint;
string provideClientKey;
string signClientKey;
string sharedInbox;
static ActorEndpoints fromJson(JSONValue json) {
ActorEndpoints endpoints = ActorEndpoints();
with (endpoints) {
proxyUrl = json.checkKey!string("proxyUrl");
oauthAuthorizationEndpoint = json.checkKey!string("oauthAuthorizationEndpoint");
oauthTokenEndpoint = json.checkKey!string("oauthTokenEndpoint");
provideClientKey = json.checkKey!string("provideClientKey");
signClientKey = json.checkKey!string("signClientKey");
sharedInbox = json.checkKey!string("sharedInbox");
}
return endpoints;
}
}

10
source/ap/errors.d Normal file
View File

@@ -0,0 +1,10 @@
module ap.errors;
/++
* Base exception for ActivityPub
+/
class APException : Exception {
this(string msg) {
super(msg);
}
}

20
source/util.d Normal file
View File

@@ -0,0 +1,20 @@
module util;
import std.format;
import std.json;
JSONValue requiredKey(string key)(JSONValue val) {
const(JSONValue)* j = key in val;
if (j)
return *j;
throw new Exception(format("Key %s not found in json", key));
}
T checkKey(T)(JSONValue val, string key) {
if (JSONValue* j = key in val)
return (*j).get!T;
return T.init;
}

View File

@@ -22,7 +22,7 @@ PRequest buildRequest(string uri, string[string] params) {
PRequest rq = PRequest();
rq.method = "GET";
rq.url = format("http://%s/.well-known/webfinger", uri);
rq.url = format("https://%s/.well-known/webfinger", uri);
rq.headers["Accept"] = AcceptedWebfingerContentType;
QueryParam[] p;
@@ -49,3 +49,12 @@ JSONValue requestAcct(string uri, string acct) {
return parseJSON(rs.responseBody().toString());
}
JSONValue requestAcct(string handle) {
string uri, acct;
uri = handle.split("@")[$ - 1];
acct = handle.split("@")[$ - 2];
return requestAcct(uri, acct);
}