Skip to content

MatejLach/activitystreams

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

activitystreams

License: AGPL v3

Easy to use parser of the Activity Streams 2.0 specification in Go, especially suitable for projects implementing ActivityPub.

πŸš€ Quick Start

Basic Usage

package main

import (
    "encoding/json"
    "fmt"

    "github.com/MatejLach/activitystreams"
)

func main() {
    // Decode a Note object
    noteJSON := `{
        "type": "Note",
        "content": "Hello, world!",
        "published": "2023-01-01T00:00:00Z"
    }`

    var note activitystreams.Note
    err := json.Unmarshal([]byte(noteJSON), &note)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Note content: %s\n", note.Content)

    // Encode it back to JSON
    encodedNote, err := json.Marshal(&note)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(encodedNote))
}

πŸ“š Core Types

Base Types

  • Object - The base ActivityStreams object type
  • Link - The base Link type
  • Actor - Actor types (Person, Organization, etc.)
  • Activity - Activity types (Create, Like, Follow, etc.)
  • IntransitiveActivity - Intransitive activities (Arrive, Travel, etc.)

Collections

  • Collection - Standard collection
  • CollectionPage - Paginated collection
  • OrderedCollection - Ordered collection
  • OrderedCollectionPage - Paginated ordered collection

Special Types

  • ObjectOrLink - Slice that can contain any ActivityStreams object or link
  • ObjectOrLinkOrString - Can hold objects/links or string URLs
  • Icons - Icon representation supporting both URL and Object forms
  • Location - Geographic location data

πŸ”§ Working with Objects

Decoding

// Decode any ActivityStreams object using the generic decoder
func decodeObject(jsonData []byte) (activitystreams.ObjectLinker, error) {
    var obj activitystreams.ObjectOrLink
    err := json.Unmarshal(jsonData, &obj)
    if err != nil {
        return nil, err
    }

    if len(obj) > 0 {
        return obj[0], nil
    }
    return nil, errors.New("empty object")
}

// Decode specific types
func decodeNote(jsonData []byte) (*activitystreams.Note, error) {
    var note activitystreams.Note
    err := json.Unmarshal(jsonData, &note)
    return &note, err
}

Encoding

// Encode any ActivityStreams object
func encodeObject(obj activitystreams.ObjectLinker) ([]byte, error) {
    return json.Marshal(obj)
}

// Using the generic encoder
func encodeGeneric[T activitystreams.ActivityStreamer](toEncode T) ([]byte, error) {
    return activitystreams.EncodeJSON(toEncode)
}

πŸ“¦ Working with Collections

πŸ“€ Heterogeneous slices

Slices can contain any mix of Object or Link (sub)types:

// ObjectOrLink handles heterogeneous arrays
var items activitystreams.ObjectOrLink
items = append(items, activitystreams.Note{Content: "Hello"})
items = append(items, activitystreams.Person{Name: "Alice"})

jsonBytes, err := json.Marshal(items)
// Results in JSON array with mixed types

ObjectOrLink Slices

// Create and populate a heterogeneous collection
var items activitystreams.ObjectOrLink

// Add different types of objects
note := activitystreams.Note{Content: "Hello"}
person := activitystreams.Person{Name: "Alice"}

items = append(items, note)
items = append(items, person)

// Encode the collection
jsonBytes, err := json.Marshal(items)

Collections with Pagination

// Create a Collection
collection := activitystreams.Collection{
    TotalItems: 100,
    Items: &activitystreams.ObjectOrLinkOrString{
        Target: activitystreams.ObjectOrLink{
            activitystreams.Note{Content: "First note"},
            activitystreams.Note{Content: "Second note"},
        },
    },
}

πŸ”„ Generic Helper Functions

DecodeJSON

// Decode any valid ActivityStreams object
note, err := activitystreams.DecodeJSON[activitystreams.Note](strings.NewReader(noteJSON))
if err != nil {
    // handle error
}

// Works with any AS type
person, err := activitystreams.DecodeJSON[activitystreams.Person](strings.NewReader(personJSON))
activity, err := activitystreams.DecodeJSON[activitystreams.Create](strings.NewReader(activityJSON))

EncodeJSON

// Generic encoder for any valid ActivityStreams object
note := activitystreams.Note{Content: "Hello"}
bytes, err := activitystreams.EncodeJSON(note)
if err != nil {
    // handle error
}

πŸ“‘ Working with Actors

Person Example

person := activitystreams.Person{
    ID:                "https://example.com/users/alice",
    Type:              "Person",
    Name:              "Alice Smith",
    PreferredUsername: "alice",
    Summary:           "A person on the web",
    Inbox: &activitystreams.StringWithOrderedCollection{
        URL: "https://example.com/users/alice/inbox",
    },
    Outbox: &activitystreams.StringWithOrderedCollection{
        URL: "https://example.com/users/alice/outbox",
    },
}

Actor Collections

// Create a followers collection
followers := activitystreams.Collection{
    TotalItems: 42,
    First: &activitystreams.StringWithCollectionPage{
        CollectionPage: activitystreams.CollectionPage{
            // pagination details
        },
        URL: "https://example.com/users/alice/followers?page=1",
    },
}

πŸ“ Working with Activities

Create Activity Example

createActivity := activitystreams.Create{
    Type: "Create",
    Actor: &activitystreams.ObjectOrLinkOrString{
        Target: activitystreams.ObjectOrLink{
            activitystreams.Person{
                ID:   "https://example.com/users/alice",
                Name: "Alice Smith",
            },
        },
    },
    ActivityObject: &activitystreams.ObjectOrLinkOrString{
        Target: activitystreams.ObjectOrLink{
            activitystreams.Note{
                Content: "Hello, world!",
                Published: &time.Now(),
            },
        },
    },
}

πŸ” Type Checking and Conversion

Using ConcreteType

func processObject(obj activitystreams.ObjectLinker) {
    reflectType, asType := activitystreams.ConcreteType(obj)
    fmt.Printf("Reflection type: %s, AS type: %s\n", reflectType, asType)

    switch asType {
    case "Note":
        if note, ok := obj.(activitystreams.Note); ok {
            // Handle Note
        }
    case "Person":
        if person, ok := obj.(activitystreams.Person); ok {
            // Handle Person
        }
    }
}

Type Assertions

// Safe type assertions
func asNote(obj activitystreams.ObjectLinker) (*activitystreams.Note, bool) {
    if n, ok := obj.(activitystreams.Note); ok {
        return &n, true
    }
    return nil, false
}

func asPerson(obj activitystreams.ObjectLinker) (*activitystreams.Person, bool) {
    if p, ok := obj.(activitystreams.Person); ok {
        return &p, true
    }
    return nil, false
}

Type Analysis

func decodePayload(jsonData []byte) (activitystreams.JsonPayload, error) {
    reader := bytes.NewReader(jsonData)
    payloadType, err := activitystreams.DecodePayloadObjectType(reader)
    if err != nil {
        return payloadType, err
    }

    switch payloadType.Type {
    case "Note":
        // Handle Note
    case "Person":
        // Handle Person
    default:
        // Handle generic object or unknown type
    }

    return payloadType, nil
}

πŸ“‹ Supported Types

Object Types

  • Object
  • Article, Document, Audio, Video, Image
  • Event, Note, Page
  • Collection, CollectionPage
  • OrderedCollection, OrderedCollectionPage
  • Location, Profile, Tombstone
  • Relationship, Question

Link Types

  • Link
  • Mention, Hashtag, PropertyValue

Actor Types

  • Application, Group, Organization, Person, Service

Activity Types

  • Accept, Add, Announce, Arrive, Block, Create, Delete
  • Dislike, Follow, Flag, Ignore, Invite, Join, Leave
  • Like, Listen, Move, Offer, Read, Reject, Remove
  • TentativeAccept, TentativeReject, Travel, Undo, Update, View

Contributing

Contributions are welcome. If you've found a bug, or have a feature request, don't hesitate to open an issue.

Releases

No releases published

Languages