116 lines
3.1 KiB
Go
116 lines
3.1 KiB
Go
|
|
package pana
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"log/slog"
|
||
|
|
|
||
|
|
ld "sourcery.dny.nu/longdistance"
|
||
|
|
"sourcery.dny.nu/pana/internal/json"
|
||
|
|
"sourcery.dny.nu/pana/internal/loader"
|
||
|
|
"sourcery.dny.nu/pana/vocab/schema"
|
||
|
|
as "sourcery.dny.nu/pana/vocab/w3/activitystreams"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Processor is used to process incoming messages and prepare outgoing messages.
|
||
|
|
//
|
||
|
|
// Your application only needs a single processor instance.
|
||
|
|
type Processor struct {
|
||
|
|
ldproc *ld.Processor
|
||
|
|
l *loader.Builtin
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewProcessor initialised a new [Processor] suitable for dealing with
|
||
|
|
// ActivityStreams messages.
|
||
|
|
//
|
||
|
|
// It uses [loader.Builtin] to retrieve contexts and does not retrieve them over
|
||
|
|
// the network.
|
||
|
|
func NewProcessor(
|
||
|
|
logger *slog.Logger,
|
||
|
|
) *Processor {
|
||
|
|
if logger == nil {
|
||
|
|
logger = slog.New(slog.DiscardHandler)
|
||
|
|
}
|
||
|
|
|
||
|
|
loader := loader.New()
|
||
|
|
return &Processor{
|
||
|
|
l: loader,
|
||
|
|
ldproc: ld.NewProcessor(
|
||
|
|
ld.WithLogger(logger),
|
||
|
|
ld.WithCompactArrays(true),
|
||
|
|
ld.WithCompactToRelative(false), // Avoids compacting to relative IRIs
|
||
|
|
ld.With10Processing(false), // Misskey seems to do some JSON-LD 1.1
|
||
|
|
ld.WithRemoteContextLoader(loader.Get),
|
||
|
|
ld.WithExcludeIRIsFromCompaction(as.PublicCollection),
|
||
|
|
ld.WithRemapPrefixIRIs("http://schema.org#", schema.Namespace),
|
||
|
|
),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Marshal takes an [Activity] and returns JSON-LD in compacted document form.
|
||
|
|
//
|
||
|
|
// This is the shape of JSON you want to exchange with other servers and
|
||
|
|
// clients.
|
||
|
|
//
|
||
|
|
// The compaction context is a JSON document representing the JSON-LD context
|
||
|
|
// that should be used for compaction.
|
||
|
|
func (p *Processor) Marshal(
|
||
|
|
compactionContext json.RawMessage,
|
||
|
|
activity Activity,
|
||
|
|
) (json.RawMessage, error) {
|
||
|
|
if compactionContext[0] == '{' {
|
||
|
|
ctx, err := json.GetContextDocument(compactionContext)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
compactionContext = ctx
|
||
|
|
}
|
||
|
|
|
||
|
|
return p.ldproc.Compact(
|
||
|
|
compactionContext,
|
||
|
|
[]ld.Node{ld.Node(activity)},
|
||
|
|
"",
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Unmarshal takes a JSON document and returns an [Activity] that represents it
|
||
|
|
// using JSON-LD expanded document form.
|
||
|
|
//
|
||
|
|
// In JSON-LD the result of expanding a document is always a list. But in the
|
||
|
|
// case of ActivityPub we only ever send out a single document at a time, so it
|
||
|
|
// returns [Activity]. If the result happens to have more than one object an
|
||
|
|
// error is raised so it doesn't go unnoticed.
|
||
|
|
func (p *Processor) Unmarshal(
|
||
|
|
document json.RawMessage,
|
||
|
|
) (*Activity, error) {
|
||
|
|
res, err := p.ldproc.Expand(document, "")
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
if lres := len(res); lres != 1 {
|
||
|
|
return nil, fmt.Errorf("expected a single document, got: %d", lres)
|
||
|
|
}
|
||
|
|
|
||
|
|
return (*Activity)(&res[0]), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// RegisterContextURL adds or overrides a context document for the specified
|
||
|
|
// remote context URL in the loader.
|
||
|
|
func (p *Processor) RegisterContextURL(
|
||
|
|
url string,
|
||
|
|
doc json.RawMessage,
|
||
|
|
) error {
|
||
|
|
return p.l.RegisterContextURL(url, doc)
|
||
|
|
}
|
||
|
|
|
||
|
|
// RegisterContextPath adds or overrides a context document for the specified
|
||
|
|
// remote path in the loader.
|
||
|
|
//
|
||
|
|
// Paths are always matches as URL suffixes.
|
||
|
|
func (p *Processor) RegisterContextPath(
|
||
|
|
path string,
|
||
|
|
doc json.RawMessage,
|
||
|
|
) error {
|
||
|
|
return p.l.RegisterContextPath(path, doc)
|
||
|
|
}
|