···11# GoAT Site
2233-GoAT Site implements [standard.site](https://standard.site/) in Go.
33+GoAT Site implements [Standard.site](https://standard.site/) in Go.
4455Use official [`bluesky-social/indigo`](https://github.com/bluesky-social/indigo/).
66+77+Main repository is hosted on [Tangled](https://tangled.org/anhgelus.world/goat-site/), an ATProto forge.
6879## Usage
810···1113go get -u tangled.org/anhgelus.world/goat-site
1214```
13151616+Each Standard.site lexicon is implemented:
1717+- `Publication` is for `site.standard.publication`;
1818+- `Document` is for `site.standard.document`;
1919+- `Subscription` is for `site.standard.graph.subscription`.
14202121+These types implement `Record`, an interface describing records.
2222+2323+You can get, list, create, update or delete them with functions.
2424+Each function starts with the action followed by the lexicon's name, e.g.,
2525+- `GetPublication` to get a publication;
2626+- `ListDocuments` to list documents;
2727+- `CreateDocument` to create a new document.
2828+2929+Currently, functions related to `Subscription` are not implemented.
3030+3131+You can [verify](https://standard.site/docs/verification/) a publication with `Publication.Verify`.
3232+3333+## Creating custom records
3434+3535+`Document.Content` is an open union: you can create your own lexicon to use it.
3636+3737+If the NSID of your lexicon is `tld.example.content` and its definition in Go is:
3838+```go
3939+type Content struct {
4040+ // Pars represents the paragraphs in [Content].
4141+ Pars []string `json:"pars"`
4242+}
4343+```
4444+4545+To use it, you have to implement `site.Record`:
4646+```go
4747+const CollectionContent = `tld.example.content`
4848+4949+func (c *Content) Type() string {
5050+ return CollectionContent
5151+}
5252+```
5353+5454+But, if you use `site.GetDocument` to retrieve one, it will return a simple `site.Document` without your custom content!
5555+The `Document.Content` field is a `site.RecordJSON`, a wrapper.
5656+You can get the type of the content with `RecordJSON.Type` and the raw bytes with `RecordJSON.Raw`.
5757+You can also directly parse your `Content` with `RecordJSON.As`:
5858+```go
5959+var doc *site.Document
6060+var c *Content
6161+// returns an error if it cannot parse or if the type is invalid
6262+err := doc.Content.As(&c)
6363+if err != nil {
6464+ panic(err)
6565+}
6666+```
6767+6868+### Marshal/Unmarshal
6969+7070+When your record is sent, it is firstly marshaled to a map.
7171+We provide `site.MarshalToMap` which works like the JSON API:
7272+```go
7373+var c *Content
7474+// mp is the map[string]any created
7575+mp, err := site.MarshalToMap(c)
7676+if err != nil {
7777+ panic(err)
7878+}
7979+/*
8080+mp = map[string]any{"content": []string{}}
8181+*/
8282+```
8383+8484+It uses the `json` tag to determine how to marshal the content.
8585+It supports `omitempty`, `string` and embedded type.
8686+8787+If you are using complexe types, you may have to implement `json.Unmarshaler` to unmarshal from JSON and
8888+`site.MarshalerMap` to marshal to a map.
8989+```go
9090+func (c *Content) MarshalMap() (map[string]any, error) {
9191+ mp := make(map[string]any, 1)
9292+ mp["foo"] = "bar"
9393+ return mp, nil
9494+}
9595+// the future call to site.MarshalToMap on *Content will return map[string]any{"foo":"bar"}.
9696+```
+1-1
document.go
···3636 // CoverImage to used for thumbnail or cover.
3737 // Less than 1MB in size.
3838 CoverImage *Blob `json:"coverImage,omitempty"`
3939- // Content is a custom [Lexicon] used to define the [Document]'s content.
3939+ // Content is a custom [Record] used to define the [Document]'s content.
4040 Content *RecordJSON `json:"content,omitempty"`
4141 // TextContent is a plaintext representation of the [Document.Content].
4242 // Should not contain markdown or other formatting.
+1
lexicons.go
···2222 CollectionBase = "site.standard"
2323 CollectionBlob = "blob"
24242525+ // TimeFormat is the standard time format specified by the ATProto.
2526 TimeFormat = "2006-01-02T15:04:05.000Z07:00"
2627)
2728