Compare commits

..

2 Enmetoj

Author SHA1 Message Date
Jaidyn Ann e0341343db Remove :UPDATE parameter from DEFINE-JSON-TYPE
It was… a bit messy on accessors. That’s more
headache than it’s worth, and it can be argued
that messing with the hierarchy like that was less
than preferable.

The LitePub classes have been redefined
accordingly as subclasses.
2024-10-20 11:25:24 -05:00
Jaidyn Ann 06de0e0191 Add a file-caching of common JSON-LD schema
So we don't need to HTTP-get common (and sometimes
down (I'm looking at you, purl.archive.org; though
it's not your fault, the DDOS attack was huge)
schemas.
2024-10-18 11:03:44 -05:00
6 changed files with 553 additions and 79 deletions

49
schema/litepub-0.1.jsonld Normal file
View File

@ -0,0 +1,49 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
"https://purl.archive.org/socialweb/webfinger",
{
"Emoji": "toot:Emoji",
"Hashtag": "as:Hashtag",
"PropertyValue": "schema:PropertyValue",
"atomUri": "ostatus:atomUri",
"conversation": {
"@id": "ostatus:conversation",
"@type": "@id"
},
"discoverable": "toot:discoverable",
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"capabilities": "litepub:capabilities",
"ostatus": "http://ostatus.org#",
"schema": "http://schema.org#",
"toot": "http://joinmastodon.org/ns#",
"fedibird": "http://fedibird.com/ns#",
"value": "schema:value",
"sensitive": "as:sensitive",
"litepub": "http://litepub.social/ns#",
"invisible": "litepub:invisible",
"directMessage": "litepub:directMessage",
"listMessage": {
"@id": "litepub:listMessage",
"@type": "@id"
},
"quoteUrl": "as:quoteUrl",
"quoteUri": "fedibird:quoteUri",
"oauthRegistrationEndpoint": {
"@id": "litepub:oauthRegistrationEndpoint",
"@type": "@id"
},
"EmojiReact": "litepub:EmojiReact",
"ChatMessage": "litepub:ChatMessage",
"alsoKnownAs": {
"@id": "as:alsoKnownAs",
"@type": "@id"
},
"vcard": "http://www.w3.org/2006/vcard/ns#",
"formerRepresentations": "litepub:formerRepresentations",
"sm": "http://smithereen.software/ns#",
"nonAnonymous": "sm:nonAnonymous"
}
]
}

View File

@ -0,0 +1,10 @@
{
"@context": {
"wf": "https://web.archive.org/web/20240820094819/https://purl.archive.org/socialweb/webfinger#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"webfinger": {
"@id": "wf:webfinger",
"@type": "xsd:string"
}
}
}

View File

@ -0,0 +1,50 @@
{
"@context": {
"id": "@id",
"type": "@type",
"dc": "http://purl.org/dc/terms/",
"sec": "https://w3id.org/security#",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"EcdsaKoblitzSignature2016": "sec:EcdsaKoblitzSignature2016",
"Ed25519Signature2018": "sec:Ed25519Signature2018",
"EncryptedMessage": "sec:EncryptedMessage",
"GraphSignature2012": "sec:GraphSignature2012",
"LinkedDataSignature2015": "sec:LinkedDataSignature2015",
"LinkedDataSignature2016": "sec:LinkedDataSignature2016",
"CryptographicKey": "sec:Key",
"authenticationTag": "sec:authenticationTag",
"canonicalizationAlgorithm": "sec:canonicalizationAlgorithm",
"cipherAlgorithm": "sec:cipherAlgorithm",
"cipherData": "sec:cipherData",
"cipherKey": "sec:cipherKey",
"created": {"@id": "dc:created", "@type": "xsd:dateTime"},
"creator": {"@id": "dc:creator", "@type": "@id"},
"digestAlgorithm": "sec:digestAlgorithm",
"digestValue": "sec:digestValue",
"domain": "sec:domain",
"encryptionKey": "sec:encryptionKey",
"expiration": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
"expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
"initializationVector": "sec:initializationVector",
"iterationCount": "sec:iterationCount",
"nonce": "sec:nonce",
"normalizationAlgorithm": "sec:normalizationAlgorithm",
"owner": {"@id": "sec:owner", "@type": "@id"},
"password": "sec:password",
"privateKey": {"@id": "sec:privateKey", "@type": "@id"},
"privateKeyPem": "sec:privateKeyPem",
"publicKey": {"@id": "sec:publicKey", "@type": "@id"},
"publicKeyBase58": "sec:publicKeyBase58",
"publicKeyPem": "sec:publicKeyPem",
"publicKeyWif": "sec:publicKeyWif",
"publicKeyService": {"@id": "sec:publicKeyService", "@type": "@id"},
"revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"},
"salt": "sec:salt",
"signature": "sec:signature",
"signatureAlgorithm": "sec:signingAlgorithm",
"signatureValue": "sec:signatureValue"
}
}

View File

@ -0,0 +1,379 @@
{
"@context": {
"@vocab": "_:",
"xsd": "http://www.w3.org/2001/XMLSchema#",
"as": "https://www.w3.org/ns/activitystreams#",
"ldp": "http://www.w3.org/ns/ldp#",
"vcard": "http://www.w3.org/2006/vcard/ns#",
"id": "@id",
"type": "@type",
"Accept": "as:Accept",
"Activity": "as:Activity",
"IntransitiveActivity": "as:IntransitiveActivity",
"Add": "as:Add",
"Announce": "as:Announce",
"Application": "as:Application",
"Arrive": "as:Arrive",
"Article": "as:Article",
"Audio": "as:Audio",
"Block": "as:Block",
"Collection": "as:Collection",
"CollectionPage": "as:CollectionPage",
"Relationship": "as:Relationship",
"Create": "as:Create",
"Delete": "as:Delete",
"Dislike": "as:Dislike",
"Document": "as:Document",
"Event": "as:Event",
"Follow": "as:Follow",
"Flag": "as:Flag",
"Group": "as:Group",
"Ignore": "as:Ignore",
"Image": "as:Image",
"Invite": "as:Invite",
"Join": "as:Join",
"Leave": "as:Leave",
"Like": "as:Like",
"Link": "as:Link",
"Mention": "as:Mention",
"Note": "as:Note",
"Object": "as:Object",
"Offer": "as:Offer",
"OrderedCollection": "as:OrderedCollection",
"OrderedCollectionPage": "as:OrderedCollectionPage",
"Organization": "as:Organization",
"Page": "as:Page",
"Person": "as:Person",
"Place": "as:Place",
"Profile": "as:Profile",
"Question": "as:Question",
"Reject": "as:Reject",
"Remove": "as:Remove",
"Service": "as:Service",
"TentativeAccept": "as:TentativeAccept",
"TentativeReject": "as:TentativeReject",
"Tombstone": "as:Tombstone",
"Undo": "as:Undo",
"Update": "as:Update",
"Video": "as:Video",
"View": "as:View",
"Listen": "as:Listen",
"Read": "as:Read",
"Move": "as:Move",
"Travel": "as:Travel",
"IsFollowing": "as:IsFollowing",
"IsFollowedBy": "as:IsFollowedBy",
"IsContact": "as:IsContact",
"IsMember": "as:IsMember",
"subject": {
"@id": "as:subject",
"@type": "@id"
},
"relationship": {
"@id": "as:relationship",
"@type": "@id"
},
"actor": {
"@id": "as:actor",
"@type": "@id"
},
"attributedTo": {
"@id": "as:attributedTo",
"@type": "@id"
},
"attachment": {
"@id": "as:attachment",
"@type": "@id"
},
"bcc": {
"@id": "as:bcc",
"@type": "@id"
},
"bto": {
"@id": "as:bto",
"@type": "@id"
},
"cc": {
"@id": "as:cc",
"@type": "@id"
},
"context": {
"@id": "as:context",
"@type": "@id"
},
"current": {
"@id": "as:current",
"@type": "@id"
},
"first": {
"@id": "as:first",
"@type": "@id"
},
"generator": {
"@id": "as:generator",
"@type": "@id"
},
"icon": {
"@id": "as:icon",
"@type": "@id"
},
"image": {
"@id": "as:image",
"@type": "@id"
},
"inReplyTo": {
"@id": "as:inReplyTo",
"@type": "@id"
},
"items": {
"@id": "as:items",
"@type": "@id"
},
"instrument": {
"@id": "as:instrument",
"@type": "@id"
},
"orderedItems": {
"@id": "as:items",
"@type": "@id",
"@container": "@list"
},
"last": {
"@id": "as:last",
"@type": "@id"
},
"location": {
"@id": "as:location",
"@type": "@id"
},
"next": {
"@id": "as:next",
"@type": "@id"
},
"object": {
"@id": "as:object",
"@type": "@id"
},
"oneOf": {
"@id": "as:oneOf",
"@type": "@id"
},
"anyOf": {
"@id": "as:anyOf",
"@type": "@id"
},
"closed": {
"@id": "as:closed",
"@type": "xsd:dateTime"
},
"origin": {
"@id": "as:origin",
"@type": "@id"
},
"accuracy": {
"@id": "as:accuracy",
"@type": "xsd:float"
},
"prev": {
"@id": "as:prev",
"@type": "@id"
},
"preview": {
"@id": "as:preview",
"@type": "@id"
},
"replies": {
"@id": "as:replies",
"@type": "@id"
},
"result": {
"@id": "as:result",
"@type": "@id"
},
"audience": {
"@id": "as:audience",
"@type": "@id"
},
"partOf": {
"@id": "as:partOf",
"@type": "@id"
},
"tag": {
"@id": "as:tag",
"@type": "@id"
},
"target": {
"@id": "as:target",
"@type": "@id"
},
"to": {
"@id": "as:to",
"@type": "@id"
},
"url": {
"@id": "as:url",
"@type": "@id"
},
"altitude": {
"@id": "as:altitude",
"@type": "xsd:float"
},
"content": "as:content",
"contentMap": {
"@id": "as:content",
"@container": "@language"
},
"name": "as:name",
"nameMap": {
"@id": "as:name",
"@container": "@language"
},
"duration": {
"@id": "as:duration",
"@type": "xsd:duration"
},
"endTime": {
"@id": "as:endTime",
"@type": "xsd:dateTime"
},
"height": {
"@id": "as:height",
"@type": "xsd:nonNegativeInteger"
},
"href": {
"@id": "as:href",
"@type": "@id"
},
"hreflang": "as:hreflang",
"latitude": {
"@id": "as:latitude",
"@type": "xsd:float"
},
"longitude": {
"@id": "as:longitude",
"@type": "xsd:float"
},
"mediaType": "as:mediaType",
"published": {
"@id": "as:published",
"@type": "xsd:dateTime"
},
"radius": {
"@id": "as:radius",
"@type": "xsd:float"
},
"rel": "as:rel",
"startIndex": {
"@id": "as:startIndex",
"@type": "xsd:nonNegativeInteger"
},
"startTime": {
"@id": "as:startTime",
"@type": "xsd:dateTime"
},
"summary": "as:summary",
"summaryMap": {
"@id": "as:summary",
"@container": "@language"
},
"totalItems": {
"@id": "as:totalItems",
"@type": "xsd:nonNegativeInteger"
},
"units": "as:units",
"updated": {
"@id": "as:updated",
"@type": "xsd:dateTime"
},
"width": {
"@id": "as:width",
"@type": "xsd:nonNegativeInteger"
},
"describes": {
"@id": "as:describes",
"@type": "@id"
},
"formerType": {
"@id": "as:formerType",
"@type": "@id"
},
"deleted": {
"@id": "as:deleted",
"@type": "xsd:dateTime"
},
"inbox": {
"@id": "ldp:inbox",
"@type": "@id"
},
"outbox": {
"@id": "as:outbox",
"@type": "@id"
},
"following": {
"@id": "as:following",
"@type": "@id"
},
"followers": {
"@id": "as:followers",
"@type": "@id"
},
"streams": {
"@id": "as:streams",
"@type": "@id"
},
"preferredUsername": "as:preferredUsername",
"endpoints": {
"@id": "as:endpoints",
"@type": "@id"
},
"uploadMedia": {
"@id": "as:uploadMedia",
"@type": "@id"
},
"proxyUrl": {
"@id": "as:proxyUrl",
"@type": "@id"
},
"liked": {
"@id": "as:liked",
"@type": "@id"
},
"oauthAuthorizationEndpoint": {
"@id": "as:oauthAuthorizationEndpoint",
"@type": "@id"
},
"oauthTokenEndpoint": {
"@id": "as:oauthTokenEndpoint",
"@type": "@id"
},
"provideClientKey": {
"@id": "as:provideClientKey",
"@type": "@id"
},
"signClientKey": {
"@id": "as:signClientKey",
"@type": "@id"
},
"sharedInbox": {
"@id": "as:sharedInbox",
"@type": "@id"
},
"Public": {
"@id": "as:Public",
"@type": "@id"
},
"source": "as:source",
"likes": {
"@id": "as:likes",
"@type": "@id"
},
"shares": {
"@id": "as:shares",
"@type": "@id"
},
"alsoKnownAs": {
"@id": "as:alsoKnownAs",
"@type": "@id"
}
}
}

View File

@ -48,14 +48,13 @@ Used during encoding an object to JSON, for finding type/property-names from cla
Keys are the type-IRI (e.g., (https://www.w3.org/ns/activitystreams#Accept), and values are an irregular association list, of the form: Keys are the type-IRI (e.g., (https://www.w3.org/ns/activitystreams#Accept), and values are an irregular association list, of the form:
((CLASS-NAME-SYMBOL . TYPE-NAME) (PROPERTY-IRI SLOT-NAME-SYMBOL . PROPERTY-NAME) )") ((CLASS-NAME-SYMBOL . TYPE-NAME) (PROPERTY-IRI SLOT-NAME-SYMBOL . PROPERTY-NAME) )")
(defvar *class-defs* (make-hash-table)
"Stores the slot definitions of classes, stored directly from DEFINE-JSON-TYPE.
Used for the :UPDATE feature of DEFINE-JSON-TYPE, so you can add a slot to a pre-existing class without having to redefine the old slots.")
(defvar *http-cache* (make-hash-table :test #'equal) (defvar *http-cache* (make-hash-table :test #'equal)
"Caches context-texts fetched over HTTP. "Caches context-texts fetched over HTTP.
Maps URLs to text-content, so we dont have to download the same context again and again.") Maps URLs to text-content, so we dont have to download the same context again and again.")
(defvar *http-cache-dirs* (list (asdf:system-relative-pathname :activity-servist/json-ld "schema"))
"A list of directories used for caching remote JSON-LD caches. The cache is read-only.")
;;; Base class ;;; Base class
@ -173,14 +172,7 @@ By default, a slot will have an accessor simply named after the slot.
Set :ACCESSOR to NIL to define no accessor at all. Set :ACCESSOR to NIL to define no accessor at all.
OPTIONS contains ordinary class options, in the format of DEFCLASS (for example, OPTIONS contains ordinary class options, in the format of DEFCLASS (for example,
:DOCUMENTATION), with one exception: The :UPDATE option. :DOCUMENTATION).
If the :UPDATE class option is non-nil, then DIRECT-SLOTS will be considered an
update to the class, and will be appended to any direct-slots defined during
previous definitions of that class done with DEFINE-JSON-TYPE.
This is for convenience, so that one doesnt have to copy an entire class
defintion over in order to add one or two slots (which is a common occurance
in the ActivityPub landscape).
Here is a brief example partially defining the Place type from ActivityStreams: Here is a brief example partially defining the Place type from ActivityStreams:
@ -191,15 +183,6 @@ Here is a brief example partially defining the “Place” type from ActivityStr
:documentation The latitude of a place.) :documentation The latitude of a place.)
(longitude longitude (longitude longitude
:documentation The longitude of a place.)))" :documentation The longitude of a place.)))"
;; If the definition is an :UPDATE, remove that from OPTIONS and merge the old
;; slots with the new.
(let ((direct-slots
(if (assoc :update options)
(progn (setf options (remove-from-alist :update options))
(merge-alists direct-slots
(gethash (car names) *class-defs*) 't))
direct-slots)))
;; Now, actually define the class, encoder, etc…
`(let ((json-class `(let ((json-class
(define-json-clos-class ,names (define-json-clos-class ,names
,(or direct-superclasses `(json-ld:object)) ,(or direct-superclasses `(json-ld:object))
@ -207,7 +190,7 @@ Here is a brief example partially defining the “Place” type from ActivityStr
,options))) ,options)))
(define-json-type-encoder ,(car names) ,direct-slots) (define-json-type-encoder ,(car names) ,direct-slots)
(register-json-type ',names (or ',direct-superclasses '(json-ld:object)) ',direct-slots ,context) (register-json-type ',names (or ',direct-superclasses '(json-ld:object)) ',direct-slots ,context)
json-class))) json-class))
(defmacro define-json-clos-class (names direct-superclasses direct-slots options) (defmacro define-json-clos-class (names direct-superclasses direct-slots options)
"Helper-macro for DEFINE-JSON-TYPE. "Helper-macro for DEFINE-JSON-TYPE.
@ -272,10 +255,7 @@ corresponding CLOS class) of a node."
(let* ((ctx (parse-context context)) (let* ((ctx (parse-context context))
(type-iri (getf (gethash (cadr names) ctx) :id)) (type-iri (getf (gethash (cadr names) ctx) :id))
(type-name (or type-iri (cadr names)))) (type-name (or type-iri (cadr names))))
;; Save the types direct-slots, in case of future :UPDATEs. ;; Save the JSON-type.
(setf (gethash (car names) *class-defs*)
direct-slots)
;; Now actually save the JSON-type.
(setf (gethash type-name *json-types*) (setf (gethash type-name *json-types*)
(json-type-registry-list names direct-superclasses ctx direct-slots)))) (json-type-registry-list names direct-superclasses ctx direct-slots))))
@ -640,31 +620,38 @@ defined in the context."))
"Makes a GET request to URI, returning the resultant string. "Makes a GET request to URI, returning the resultant string.
Each resultant string is cached in the *HTTP-CACHE* global variable; if the same Each resultant string is cached in the *HTTP-CACHE* global variable; if the same
URI is requested more than once, the cached version will subsequently be URI is requested more than once, the cached version will subsequently be
returned." returned.
(or (gethash uri *http-cache*) In addition, there is a filesystem cache; if a file named afer the URI
(setf (gethash uri *http-cache*) (sans the protocol, replacing slashes '/' with hyphens '-') is found in
the directories *HTTP-CACHE-DIRS*, its contents will be returned instead."
(or (gethash uri *http-cache*) ; Try our downloaded/read-file cache.
(setf (gethash uri *http-cache*) ; Try our read-only filesystem cache...
(let* ((file-leaf (str:replace-all "/" "-" (uri-sans-scheme uri)))
(cached-filepath (find-file file-leaf *http-cache-dirs*)))
(when cached-filepath
(alexandria:read-file-into-string cached-filepath))))
(setf (gethash uri *http-cache*) ; If not cached, download & cache it.
(http-get uri :headers headers)))) (http-get uri :headers headers))))
(defun find-file (file-leaf dirs)
"Search for a file of the given name FILE-LEAF within directories DIRS.
Returns the first found matching file."
(or (find file-leaf (uiop:directory-files (format nil "~A/*" (car dirs)))
:test (lambda (a b)
(equal a (file-namestring b))))
(unless (not (cdr dirs))
(get-file-from-dirs file-leaf (cdr dirs)))))
(defun http-get (uri &key headers) (defun http-get (uri &key headers)
"Makes a GET request to URI, returning the resultant string." "Makes a GET request to URI, returning the resultant string."
(dexador:get uri :headers headers :force-string 't)) (dexador:get uri :headers headers :force-string 't))
(defun remove-from-alist (item alist) (defun uri-sans-scheme (uri)
"Removes the cell corresponding to ITEM from an association list." "Returns a URI string without its scheme."
(remove item alist (str:replace-all
:test (lambda (key cell) (format nil "~A://" (quri:uri-scheme (quri:uri "https://dad.com/")))
(eq (car cell) key)))) ""
uri))
(defun merge-alists (a b &optional clobberp)
"Merge two association lists, adding all items of A to B not pre-existing in B.
If CLOBBERP is set, pre-existing items of B will be overwritten regardless."
(loop for cell in a
do (let ((b-has-item-p (assoc (car cell) b)))
(cond ((and b-has-item-p clobberp)
(setf (cdr (assoc (car cell) b)) (cdr cell)))
((not b-has-item-p)
(alexandria:appendf b (list cell))))))
b)
(defun plist-keys (plist) (defun plist-keys (plist)
"Return a list of keys in a property list." "Return a list of keys in a property list."

View File

@ -20,7 +20,7 @@
(:nicknames "AS/V/LP" "LITEPUB") (:nicknames "AS/V/LP" "LITEPUB")
(:export (:export
;; Classes ;; Classes
:object :activity :note :object :update
:chat-message :emoji :emoji-react :hashtag :property-value :chat-message :emoji :emoji-react :hashtag :property-value
;; Slots/Accessors ;; Slots/Accessors
:also-known-as :atom-uri :capabilities :conversation :direct-message-p :also-known-as :atom-uri :capabilities :conversation :direct-message-p
@ -43,11 +43,7 @@ Defaults to a copy at jam.xwx.moe — because why not? ¯\_(ツ)_/¯")
;;; Core types ;;; Core types
;;; ———————————————————————————————————————— ;;; ————————————————————————————————————————
(defclass object () (json-ld:define-json-type (object "Object") (as/v/a:object) *litepub-uri*
()
(:documentation "The base class used for Litepub objects."))
(json-ld:define-json-type (as/v/a:object "Object") (as/jld:object litepub:object) *litepub-uri*
((atom-uri ((atom-uri
"atomUri" "atomUri"
:documentation "A string containing a URI to an Atom-feed alternative representation of an object. :documentation "A string containing a URI to an Atom-feed alternative representation of an object.
@ -75,27 +71,33 @@ Generally contains the properties “id”, “owner”, “publicKeyPem”.")
;; https://docs.joinmastodon.org/spec/activitypub/#discoverable ;; https://docs.joinmastodon.org/spec/activitypub/#discoverable
(discoverablep (discoverablep
"discoverable" "discoverable"
:accessor actor-discoverable-p
:documentation "A boolean value reflecting whether or not an Actors profile should be publically discoverable.") :documentation "A boolean value reflecting whether or not an Actors profile should be publically discoverable.")
;; https://docs.joinmastodon.org/spec/activitypub/#as ;; https://docs.joinmastodon.org/spec/activitypub/#as
(manually-approves-followers-p (manually-approves-followers-p
"manuallyApprovesFollowers" "manuallyApprovesFollowers"
:accessor actor-manually-approves-followers-p
:documentation "A boolean value, communicating whether or not an Actor screens follow-requests.") :documentation "A boolean value, communicating whether or not an Actor screens follow-requests.")
;; https://docs.joinmastodon.org/spec/activitypub/#Move ;; https://docs.joinmastodon.org/spec/activitypub/#Move
(also-known-as (also-known-as
"alsoKnownAs" "alsoKnownAs"
:accessor actor-also-known-as
:documentation "When moving between two accounts, the old account sets this property to the URI of the new account.") :documentation "When moving between two accounts, the old account sets this property to the URI of the new account.")
(capabilities (capabilities
"capabilities" "capabilities"
:accessor actor-capabilities
:documentation "Contains a hash-table of capability-names mapped to a boolean, marking this Actors (servers) support of capability. :documentation "Contains a hash-table of capability-names mapped to a boolean, marking this Actors (servers) support of capability.
One known capabilitity-name is Pleromas acceptsChatMessages.")) One known capabilitity-name is Pleromas acceptsChatMessages.")))
(:update 't))
(json-ld:define-json-type (as/v/a:activity "Activity") (as/v/a:object) *litepub-uri* (defmethod json-ld:@context ((obj litepub:object))
(let ((class (class-name (class-of obj))))
(case class
;; Only use LitePub context for newly-defined classes.
(('property-value 'emoji-react 'chat-message 'hashtag 'emoji)
*litepub-uri*)
;; TODO: Also use LitePub context for old classes when new slots are used.
(otherwise
(call-next-method)))))
(json-ld:define-json-type (activity "Activity") (as/v/a:activity object) *litepub-uri*
(;; https://blog.dereferenced.org/leveraging-json-ld-compound-typing-for-behavioural-hinting-in-activitypub (;; https://blog.dereferenced.org/leveraging-json-ld-compound-typing-for-behavioural-hinting-in-activitypub
(invisiblep (invisiblep
"invisible" "invisible"
@ -103,8 +105,7 @@ One known capabilitity-name is Pleromas “acceptsChatMessages”."))
Potentially deprecated/very uncommon.") Potentially deprecated/very uncommon.")
(list-message (list-message
"list-message")) "list-message"))
(:documentation "An Activity is a subtype of Object that describes some form of action that may happen, is currently happening, or has already happened. The Activity type itself serves as an abstract base type for all types of activities. It is important to note that the Activity type itself does not carry any specific semantics about the kind of action being taken.") (:documentation "An Activity is a subtype of Object that describes some form of action that may happen, is currently happening, or has already happened. The Activity type itself serves as an abstract base type for all types of activities. It is important to note that the Activity type itself does not carry any specific semantics about the kind of action being taken."))
(:update 't))
;; https://schema.org/PropertyValue ;; https://schema.org/PropertyValue
@ -121,19 +122,18 @@ Potentially deprecated/very uncommon.")
;;; Extended Activity types ;;; Extended Activity types
;;; ———————————————————————————————————————— ;;; ————————————————————————————————————————
(json-ld:define-json-type (as/v/a:update "Update") (as/v/a:activity) *litepub-uri* (json-ld:define-json-type (update "Update") (as/v/a:update activity) *litepub-uri*
(;; https://ostatus.github.io/spec/OStatus%201.0%20Draft%202.html#rfc.section.6 (;; https://ostatus.github.io/spec/OStatus%201.0%20Draft%202.html#rfc.section.6
(conversation (conversation
"conversation" "conversation"
:documentation "When an update is part of a distributed conversation, this is the URI of that conversation. :documentation "When an update is part of a distributed conversation, this is the URI of that conversation.
Likely deprecated/highly uncommon.")) Likely deprecated/highly uncommon."))
(:documentation "Indicates that the actor has updated the object. Note, however, that this vocabulary does not define a mechanism for describing the actual set of modifications made to object. (:documentation "Indicates that the actor has updated the object. Note, however, that this vocabulary does not define a mechanism for describing the actual set of modifications made to object.
The target and origin typically have no defined meaning.") The target and origin typically have no defined meaning."))
(:update 't))
;; https://codeberg.org/fediverse/fep/src/branch/main/fep/c0e0/fep-c0e0.md ;; https://codeberg.org/fediverse/fep/src/branch/main/fep/c0e0/fep-c0e0.md
(json-ld:define-json-type (emoji-react "EmojiReact") (as/v/a:like) *litepub-uri* (json-ld:define-json-type (emoji-react "EmojiReact") (as/v/a:like activity) *litepub-uri*
() ()
(:documentation "This activity is similar to Like activity. In addition to standard properties of Like activity, EmojiReact activity MUST have a content property. Reaction content MUST be either a single unicode grapheme, or a shortcode of a custom emoji. If custom emoji is used, EmojiReact activity MUST have a tag property containing a single Emoji object.")) (:documentation "This activity is similar to Like activity. In addition to standard properties of Like activity, EmojiReact activity MUST have a content property. Reaction content MUST be either a single unicode grapheme, or a shortcode of a custom emoji. If custom emoji is used, EmojiReact activity MUST have a tag property containing a single Emoji object."))
@ -142,12 +142,12 @@ The target and origin typically have no defined meaning.")
;;; Extended Object types ;;; Extended Object types
;;; ———————————————————————————————————————— ;;; ————————————————————————————————————————
;; https://docs.joinmastodon.org/spec/activitypub/#Emoji ;; https://docs.joinmastodon.org/spec/activitypub/#Emoji
(json-ld:define-json-type (emoji "Emoji") (as/v/a:object) *litepub-uri* (json-ld:define-json-type (emoji "Emoji") (object) *litepub-uri*
() ()
(:documentation "Represents a custom-emoji, with a shortcode (NAME), ID, and ICON (containing MEDIA-TYPE and URL).")) (:documentation "Represents a custom-emoji, with a shortcode (NAME), ID, and ICON (containing MEDIA-TYPE and URL)."))
(json-ld:define-json-type (as/v/a:note "Note") (as/v/a:object) *litepub-uri* (json-ld:define-json-type (note "Note") (as/v/a:note object) *litepub-uri*
(;; https://misskey-hub.net/ns#_misskey_quote (;; https://misskey-hub.net/ns#_misskey_quote
(quote-url (quote-url
"quoteUrl" "quoteUrl"
@ -164,12 +164,11 @@ It is, however, unclear which one will win out in the end. The implementer prefe
Effectively equivalent to QUOTE-URL. Effectively equivalent to QUOTE-URL.
One of QUOTE-URL (as:quoteUrl) or QUOTE-URI (fedibird:quoteUri) is to be deprecated, and the other ought be preferred. One of QUOTE-URL (as:quoteUrl) or QUOTE-URI (fedibird:quoteUri) is to be deprecated, and the other ought be preferred.
It is, however, unclear which one will win out in the end. The implementer prefers QUOTE-URL.")) It is, however, unclear which one will win out in the end. The implementer prefers QUOTE-URL."))
(:documentation "Represents a short written work typically less than a single paragraph in length.") (:documentation "Represents a short written work typically less than a single paragraph in length."))
(:update 't))
;; https://docs-develop.pleroma.social/backend/development/ap_extensions/#chatmessages ;; https://docs-develop.pleroma.social/backend/development/ap_extensions/#chatmessages
(json-ld:define-json-type (chat-message "ChatMessage") (as/v/a:note) *litepub-uri* (json-ld:define-json-type (chat-message "ChatMessage") (note) *litepub-uri*
() ()
(:documentation "Represents a private and one-on-one chat-message. (:documentation "Represents a private and one-on-one chat-message.
Similar to Notes in creation and use, but TO may contain only one recipient. Similar to Notes in creation and use, but TO may contain only one recipient.
@ -181,12 +180,12 @@ Potentially very uncommon — it is used by at least Pleroma."))
Will set/get the value of either QUOTE-URL or QUOTE-URI, depending on which is currently in use. Will set/get the value of either QUOTE-URL or QUOTE-URI, depending on which is currently in use.
In case of doubt, QUOTE-URL is preferred.")) In case of doubt, QUOTE-URL is preferred."))
(defmethod note-quote-url ((obj as/v/a:note)) (defmethod note-quote-url ((obj note))
(or (and (slot-boundp note 'quote-url) (slot-value note 'quote-url)) (or (and (slot-boundp note 'quote-url) (slot-value note 'quote-url))
(and (slot-boundp note 'quote-uri) (slot-value note 'quote-uri)))) (and (slot-boundp note 'quote-uri) (slot-value note 'quote-uri))))
(defgeneric (setf note-quote-url) (obj value)) (defgeneric (setf note-quote-url) (obj value))
(defmethod (setf note-quote-url) ((obj as/v/a:note) value) (defmethod (setf note-quote-url) ((obj note) value)
(if (slot-boundp note 'quote-uri) (if (slot-boundp note 'quote-uri)
(setf (slot-value note 'quote-uri) value) (setf (slot-value note 'quote-uri) value)
(setf (slot-value note 'quote-url) value))) (setf (slot-value note 'quote-url) value)))
@ -196,6 +195,6 @@ In case of doubt, QUOTE-URL is preferred."))
;;; Extended Link types ;;; Extended Link types
;;; ———————————————————————————————————————— ;;; ————————————————————————————————————————
;; https://docs.joinmastodon.org/spec/activitypub/#Hashtag ;; https://docs.joinmastodon.org/spec/activitypub/#Hashtag
(json-ld:define-json-type (hashtag "Hashtag") (as/v/a:link litepub:object) *litepub-uri* (json-ld:define-json-type (hashtag "Hashtag") (as/v/a:link object) *litepub-uri*
() ()
(:documentation "Similar to Mentions, a Hashtag is used to link a post to given topics. Should be stored in a TAG slot, and contain NAME (#hashtag) and HREF (link to a servers hashtag listing).")) (:documentation "Similar to Mentions, a Hashtag is used to link a post to given topics. Should be stored in a TAG slot, and contain NAME (#hashtag) and HREF (link to a servers hashtag listing)."))