diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml
deleted file mode 100644
index 8b5eaef..0000000
--- a/.forgejo/workflows/build.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-on:
- push:
- branches:
- - main
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout repo
- uses: actions/checkout@v4
- - name: Login to Docker Hub
- uses: https://github.com/docker/login-action@v3
- with:
- registry: git.zio.sh
- username: ${{ secrets.REPO_USER }}
- password: ${{ secrets.REPO_PASS }}
- - name: Set up Docker Build Push Action
- uses: https://github.com/docker/build-push-action@v2
- with:
- tags: |
- git.zio.sh/astra/bsky2tg:latest
- git.zio.sh/astra/bsky2tg:${{ github.sha }}
- push: true
- load: false
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 2dadb1b..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,15 +0,0 @@
-FROM golang:alpine AS builder
-
-WORKDIR /go/src/git.zio.sh/bsky2tg
-COPY . .
-
-RUN apk update && \
- apk add --no-cache git bash && \
- go get -d -v ./... && \
- go install
-
-FROM alpine:latest
-
-COPY --from=builder /go/bin/bsky2tg /usr/local/bin/bsky2tg
-
-CMD ["bsky2tg"]
\ No newline at end of file
diff --git a/README.md b/README.md
index 8ee216b..b61cf15 100644
--- a/README.md
+++ b/README.md
@@ -16,12 +16,6 @@ BSKY_HANDLE=
BSKY_PASSWORD=
```
-If you use a different Telegram bot endpoint, you can set it with
-
-```properties
-TG_API_ENDPOINT=https://api.domain.com/bot%s/%s
-```
-
To run:
```bash
diff --git a/bsky/bluesky.go b/bsky/bluesky.go
index 1ca7bd5..ae8d5cf 100644
--- a/bsky/bluesky.go
+++ b/bsky/bluesky.go
@@ -127,7 +127,7 @@ func (bluesky *Bluesky) CheckSessionValid() {
type TelegramRecord struct {
ChannelID int64 `json:"channel_id"`
- MessageID []int `json:"message_id"`
+ MessageID int `json:"message_id"`
Link *Link `json:"link"`
Error string `json:"error"`
Message string `json:"message"`
@@ -263,17 +263,3 @@ type Records struct {
Cid string `json:"cid"`
Value Value `json:"value"`
}
-
-func (bluesky *Bluesky) FetchPost(did string, rkey string) FetchedPost {
- resp := &struct {
- Posts []FetchedPost `json:"posts"`
- }{}
- params := struct {
- URIs string `url:"uris"`
- }{
- URIs: fmt.Sprintf("at://%s/app.bsky.feed.post/%s", did, rkey),
- }
- bluesky.sling.New().Base("https://public.api.bsky.app").
- Get("/xrpc/app.bsky.feed.getPosts").QueryStruct(¶ms).Receive(resp, resp)
- return resp.Posts[0]
-}
diff --git a/bsky/client.go b/bsky/client.go
index c11661d..34047a4 100644
--- a/bsky/client.go
+++ b/bsky/client.go
@@ -28,7 +28,7 @@ func NewBSky() *BSky {
}
}
-func (b *BSky) ResolveHandle(handle string) (string, error) {
+func (b *BSky) getPDS() error {
httpClient := &http.Client{Timeout: 3 * time.Second}
resp := new(BSkySessionResponse)
errResp := &struct {
@@ -38,29 +38,23 @@ func (b *BSky) ResolveHandle(handle string) (string, error) {
params := struct {
Handle string `url:"handle"`
}{
- Handle: handle,
+ Handle: b.Bluesky.Cfg.Handle,
}
sling.New().Base("https://public.api.bsky.app/").Client(httpClient).
Get("/xrpc/com.atproto.identity.resolveHandle").QueryStruct(params).
Receive(resp, errResp)
if errResp.Error != "" {
- return "", errors.New(errResp.Message)
+ return errors.New(errResp.Message)
}
- return resp.DID, nil
-}
-
-func (b *BSky) getPDS() error {
- did, _ := b.ResolveHandle(b.Bluesky.Cfg.Handle)
-
var didURL url.URL
- if strings.HasPrefix(did, "did:web:") {
- didURL.Host = "https://" + did[8:]
+ if strings.HasPrefix(resp.DID, "did:web:") {
+ didURL.Host = "https://" + resp.DID[8:]
didURL.Path = "/.well-known/did.json"
- } else if strings.HasPrefix(did, "did:plc:") {
+ } else if strings.HasPrefix(resp.DID, "did:plc:") {
didURL.Host = "https://plc.directory"
- didURL.Path = "/" + did
+ didURL.Path = "/" + resp.DID
} else {
return errors.New("DID is not supported")
}
@@ -110,7 +104,7 @@ func (b *BSky) Auth(authData []string) error {
b.Bluesky.Cfg.AppPassword = authData[1]
err = b.Bluesky.CreateSession(b.Bluesky.Cfg)
if err != nil {
- return fmt.Errorf("unable to auth: %s", err)
+ return errors.New(fmt.Sprintf("unable to auth: %s", err))
}
b.Bluesky.Cfg.AppPassword = "" // we don't need to save this
PersistAuthSession(b.Bluesky.Cfg)
diff --git a/bsky/parse.go b/bsky/parse.go
index a06f131..1560be2 100644
--- a/bsky/parse.go
+++ b/bsky/parse.go
@@ -18,50 +18,42 @@ type Post struct {
Facets *[]Facets `json:"facets,omitempty"`
CreatedAt time.Time `json:"createdAt"`
}
-
type Ref struct {
Link string `json:"$link,omitempty"`
}
-
type Thumb struct {
Type string `json:"$type,omitempty"`
Ref *Ref `json:"ref,omitempty"`
MimeType string `json:"mimeType,omitempty"`
Size int `json:"size,omitempty"`
}
-
type External struct {
URI string `json:"uri,omitempty"`
Thumb *Thumb `json:"thumb,omitempty"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
}
-
type Video struct {
Type string `json:"$type,omitempty"`
Ref *Ref `json:"ref,omitempty"`
MimeType string `json:"mimeType,omitempty"`
Size int `json:"size,omitempty"`
}
-
type Image struct {
Type string `json:"$type,omitempty"`
Ref *Ref `json:"ref,omitempty"`
MimeType string `json:"mimeType,omitempty"`
Size int `json:"size,omitempty"`
}
-
type AspectRatio struct {
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
}
-
type Images struct {
Alt string `json:"alt,omitempty"`
Image *Image `json:"image,omitempty"`
AspectRatio *AspectRatio `json:"aspectRatio,omitempty"`
}
-
type Media struct {
Type string `json:"$type,omitempty"`
External *External `json:"external,omitempty"`
@@ -69,19 +61,16 @@ type Media struct {
Images *[]Images `json:"images,omitempty"`
AspectRatio *AspectRatio `json:"aspectRatio,omitempty"`
}
-
type Record struct {
Cid string `json:"cid,omitempty"`
URI string `json:"uri,omitempty"`
}
-
type PostRecord struct {
Type string `json:"$type,omitempty"`
Cid string `json:"cid,omitempty"`
URI string `json:"uri,omitempty"`
Record *Record `json:"record,omitempty"`
}
-
type Embed struct {
Type string `json:"$type,omitempty"`
Media *Media `json:"media,omitempty"`
@@ -90,59 +79,35 @@ type Embed struct {
Record *PostRecord `json:"record,omitempty"`
External *External `json:"external,omitempty"`
}
-
type Values struct {
Val string `json:"val,omitempty"`
}
-
type Labels struct {
Type string `json:"$type,omitempty"`
Values *[]Values `json:"values,omitempty"`
}
-
type Root struct {
Cid string `json:"cid,omitempty"`
URI string `json:"uri,omitempty"`
}
-
-func (r *Root) GetDID() string {
- return strings.Split(r.URI, "/")[2]
-}
-
-func (r *Root) GetRKey() string {
- return strings.Split(r.URI, "/")[4]
-}
-
type Parent struct {
Cid string `json:"cid,omitempty"`
URI string `json:"uri,omitempty"`
}
-
-func (p *Parent) GetDID() string {
- return strings.Split(p.URI, "/")[2]
-}
-
-func (p *Parent) GetRKey() string {
- return strings.Split(p.URI, "/")[4]
-}
-
type Reply struct {
Root *Root `json:"root,omitempty"`
Parent *Parent `json:"parent,omitempty"`
}
-
type Index struct {
ByteEnd int `json:"byteEnd,omitempty"`
ByteStart int `json:"byteStart,omitempty"`
}
-
type Features struct {
Did string `json:"did,omitempty"`
URI string `json:"uri,omitempty"`
Tag string `json:"tag,omitempty"`
Type string `json:"$type,omitempty"`
}
-
type Facets struct {
Type string `json:"$type"`
Index *Index `json:"index,omitempty"`
@@ -153,166 +118,11 @@ type ParsedEmbeds struct {
Type string
MimeType string
Ref string
- Cid string
URI string
Width int64
Height int64
}
-type FetchedPost struct {
- URI string `json:"uri"`
- Cid string `json:"cid"`
- Author struct {
- Did string `json:"did"`
- Handle string `json:"handle"`
- DisplayName string `json:"displayName"`
- Avatar string `json:"avatar"`
- Associated struct {
- Chat struct {
- AllowIncoming string `json:"allowIncoming"`
- } `json:"chat"`
- } `json:"associated"`
- Labels []interface{} `json:"labels"`
- CreatedAt time.Time `json:"createdAt"`
- } `json:"author"`
- Record *Post `json:"record"`
- // Record struct {
- // Type string `json:"$type"`
- // CreatedAt time.Time `json:"createdAt"`
- // Embed struct {
- // Type string `json:"$type"`
- // Media struct {
- // Type string `json:"$type"`
- // Images []struct {
- // Alt string `json:"alt"`
- // AspectRatio struct {
- // Height int `json:"height"`
- // Width int `json:"width"`
- // } `json:"aspectRatio"`
- // Image struct {
- // Type string `json:"$type"`
- // Ref struct {
- // Link string `json:"$link"`
- // } `json:"ref"`
- // MimeType string `json:"mimeType"`
- // Size int `json:"size"`
- // } `json:"image"`
- // } `json:"images"`
- // } `json:"media"`
- // Record struct {
- // Type string `json:"$type"`
- // Record struct {
- // Cid string `json:"cid"`
- // URI string `json:"uri"`
- // } `json:"record"`
- // } `json:"record"`
- // } `json:"embed"`
- // Labels struct {
- // Type string `json:"$type"`
- // Values []struct {
- // Val string `json:"val"`
- // } `json:"values"`
- // } `json:"labels"`
- // Langs []string `json:"langs"`
- // Text string `json:"text"`
- // } `json:"record"`
- Embed struct {
- Type string `json:"$type"`
- Media struct {
- Type string `json:"$type"`
- Images []struct {
- Thumb string `json:"thumb"`
- Fullsize string `json:"fullsize"`
- Alt string `json:"alt"`
- AspectRatio struct {
- Height int `json:"height"`
- Width int `json:"width"`
- } `json:"aspectRatio"`
- } `json:"images"`
- } `json:"media"`
- Record struct {
- Record struct {
- Type string `json:"$type"`
- URI string `json:"uri"`
- Cid string `json:"cid"`
- Author struct {
- Did string `json:"did"`
- Handle string `json:"handle"`
- DisplayName string `json:"displayName"`
- Avatar string `json:"avatar"`
- Associated struct {
- Chat struct {
- AllowIncoming string `json:"allowIncoming"`
- } `json:"chat"`
- } `json:"associated"`
- Labels []interface{} `json:"labels"`
- CreatedAt time.Time `json:"createdAt"`
- } `json:"author"`
- Value struct {
- Type string `json:"$type"`
- CreatedAt time.Time `json:"createdAt"`
- Embed struct {
- Type string `json:"$type"`
- AspectRatio struct {
- Height int `json:"height"`
- Width int `json:"width"`
- } `json:"aspectRatio"`
- Video struct {
- Type string `json:"$type"`
- Ref struct {
- Link string `json:"$link"`
- } `json:"ref"`
- MimeType string `json:"mimeType"`
- Size int `json:"size"`
- } `json:"video"`
- } `json:"embed"`
- Facets []struct {
- Type string `json:"$type"`
- Features []struct {
- Type string `json:"$type"`
- Did string `json:"did"`
- } `json:"features"`
- Index struct {
- ByteEnd int `json:"byteEnd"`
- ByteStart int `json:"byteStart"`
- } `json:"index"`
- } `json:"facets"`
- Langs []string `json:"langs"`
- Text string `json:"text"`
- } `json:"value"`
- Labels []interface{} `json:"labels"`
- LikeCount int `json:"likeCount"`
- ReplyCount int `json:"replyCount"`
- RepostCount int `json:"repostCount"`
- QuoteCount int `json:"quoteCount"`
- IndexedAt time.Time `json:"indexedAt"`
- Embeds []struct {
- Type string `json:"$type"`
- Cid string `json:"cid"`
- Playlist string `json:"playlist"`
- Thumbnail string `json:"thumbnail"`
- AspectRatio struct {
- Height int `json:"height"`
- Width int `json:"width"`
- } `json:"aspectRatio"`
- } `json:"embeds"`
- } `json:"record"`
- } `json:"record"`
- } `json:"embed,omitempty"`
- ReplyCount int `json:"replyCount"`
- RepostCount int `json:"repostCount"`
- LikeCount int `json:"likeCount"`
- QuoteCount int `json:"quoteCount"`
- IndexedAt time.Time `json:"indexedAt"`
- Labels []struct {
- Src string `json:"src"`
- URI string `json:"uri"`
- Cid string `json:"cid"`
- Val string `json:"val"`
- Cts time.Time `json:"cts"`
- } `json:"labels"`
-}
-
func (b *BSky) ParsePost(post []byte) (*Post, error) {
var p = &Post{}
err := json.Unmarshal(post, &p)
@@ -350,10 +160,12 @@ func (post *Post) ProcessFacets(aliases []Records) string {
switch feature.Type {
case "app.bsky.richtext.facet#mention":
link := fmt.Sprintf(`%s`, feature.Did, post.Text[start:end])
- for _, alias := range aliases {
- if alias.Value.Subject == feature.Did {
- link = fmt.Sprintf(`%s`,
- strings.SplitN(alias.Value.Target, "#", 2)[0], strings.SplitN(alias.Value.Target, "#", 2)[1])
+ if aliases != nil {
+ for _, alias := range aliases {
+ if alias.Value.Subject == feature.Did {
+ link = fmt.Sprintf(`%s`,
+ strings.SplitN(alias.Value.Target, "#", 2)[0], strings.SplitN(alias.Value.Target, "#", 2)[1])
+ }
}
}
result.WriteString(link)
diff --git a/main.go b/main.go
index 2f4ac7e..b45363f 100644
--- a/main.go
+++ b/main.go
@@ -3,8 +3,6 @@ package main
import (
"bytes"
"context"
- "encoding/json"
- "flag"
"fmt"
"image/jpeg"
"io"
@@ -13,7 +11,6 @@ import (
"net/http"
"net/url"
"os"
- "regexp"
"strconv"
"strings"
"time"
@@ -21,6 +18,7 @@ import (
"git.zio.sh/astra/bsky2tg/bsky"
tgbotapi "github.com/OvyFlash/telegram-bot-api"
+ // apibsky "github.com/bluesky-social/indigo/api/bsky"
"github.com/bluesky-social/jetstream/pkg/client"
"github.com/bluesky-social/jetstream/pkg/client/schedulers/sequential"
"github.com/bluesky-social/jetstream/pkg/models"
@@ -41,14 +39,7 @@ type handler struct {
bsky *bsky.BSky
}
-var (
- post = flag.String("post", "", "URL to a BlueSky post")
- delete = flag.Bool("delete", false, "true/false to delete post")
-)
-
func main() {
- flag.Parse()
-
var handle = os.Getenv("BSKY_HANDLE")
var password = os.Getenv("BSKY_PASSWORD")
bskyClient := bsky.NewBSky()
@@ -57,72 +48,6 @@ func main() {
log.Fatal(err, ". please set BSKY_HANDLE and BSKY_PASSWORD env variables")
}
- h := &handler{
- seenSeqs: make(map[int64]struct{}),
- bsky: bskyClient,
- }
-
- endpoint := "https://api.telegram.org/bot%s/%s"
- if os.Getenv("TG_API_ENDPOINT") != "" {
- endpoint = os.Getenv("TG_API_ENDPOINT")
- }
- bot, err := tgbotapi.NewBotAPIWithAPIEndpoint(os.Getenv("TG_TOKEN"), endpoint)
- if err != nil {
- panic(err)
- }
- h.tg = bot
-
- if os.Getenv("TG_CHANNEL_ID") == "" {
- log.Fatal("TG_CHANNEL_ID is not set")
- }
-
- if *post != "" {
- r := regexp.MustCompile(`^https:\/\/.*?\/profile\/(.*?)\/post\/(.*?)$`)
- s := r.FindStringSubmatch(*post)
- handle := s[1]
- if s[1][0:4] != "did:" {
- handle, _ = bskyClient.ResolveHandle(s[1])
- }
-
- if handle != bskyClient.Bluesky.Cfg.DID {
- log.Fatal("Unable to send posts from other accounts")
- }
-
- tgpost, tgposterr := h.bsky.Bluesky.GetTelegramData(s[2])
- if *delete {
- if tgposterr == "" {
- log.Printf("Found post %s in channel %d, deleting", s[2], tgpost.ChannelID)
- m := tgbotapi.NewDeleteMessages(tgpost.ChannelID, tgpost.MessageID)
- h.tg.Send(m)
- h.bsky.Bluesky.DeleteRecord([]string{s[2], s[1], "blue.zio.bsky2tg.post"})
- } else {
- log.Printf("Unable to find post %s on PDS", s[2])
- }
- return
- }
-
- if tgpost.ChannelID != 0 {
- log.Printf("Post %s already sent to channel %d, exiting", s[2], tgpost.ChannelID)
- return
- }
-
- postJSON := bskyClient.Bluesky.FetchPost(handle, s[2])
- p, _ := json.Marshal(postJSON.Record)
- h.ProcessPost(&models.Event{
- Did: postJSON.Author.Did,
- TimeUS: postJSON.Record.CreatedAt.Unix(),
- Kind: "",
- Commit: &models.Commit{
- CID: postJSON.Cid,
- Operation: "create",
- RKey: strings.Split(postJSON.URI, "/")[4],
- Collection: "app.bsky.feed.post",
- Record: p,
- },
- })
- return
- }
-
ctx := context.Background()
slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug.Level(),
@@ -135,6 +60,11 @@ func main() {
config.WantedDids = []string{bskyClient.Bluesky.Cfg.DID}
config.Compress = true
+ h := &handler{
+ seenSeqs: make(map[int64]struct{}),
+ bsky: bskyClient,
+ }
+
scheduler := sequential.NewScheduler("jetstream_localdev", logger, h.HandleEvent)
c, err := client.NewClient(config, logger, scheduler)
@@ -142,6 +72,57 @@ func main() {
log.Fatalf("failed to create client: %v", err)
}
+ bot, err := tgbotapi.NewBotAPIWithAPIEndpoint(os.Getenv("TG_TOKEN"), "https://bot.astra.blue/bot%s/%s")
+ if err != nil {
+ panic(err)
+ }
+ h.tg = bot
+
+ if os.Getenv("TG_CHANNEL_ID") == "" {
+ log.Fatal("TG_CHANNEL_ID is not set")
+ }
+
+ // ------------------------------------------------------------------------------
+ // file, err := os.Open("posts.json")
+ // if err != nil {
+ // fmt.Printf("Error opening file: %v\n", err)
+ // return
+ // }
+ // defer file.Close()
+ // byteValue, err := io.ReadAll(file)
+ // if err != nil {
+ // fmt.Printf("Error reading file: %v\n", err)
+ // return
+ // }
+
+ // var posts = struct {
+ // Records []struct {
+ // URI string `json:"uri"`
+ // CID string `json:"cid"`
+ // Value *bsky.Post `json:"value"`
+ // } `json:"records"`
+ // }{}
+
+ // // 4. Unmarshal (decode) the JSON data into the struct
+ // err = json.Unmarshal(byteValue, &posts)
+ // if err != nil {
+ // fmt.Printf("Error unmarshaling JSON: %v\n", err)
+ // return
+ // }
+ // for _, post := range posts.Records {
+ // log.Printf("post: %s\n", post.Value.ProcessFacets(h.bsky.Bluesky.FetchAliases()))
+ // s, _ := json.Marshal(post.Value)
+ // h.ProcessPost(&models.Event{Did: bskyClient.Bluesky.Cfg.DID, Commit: &models.Commit{
+ // Record: s,
+ // RKey: strings.Split(post.URI, "/")[4],
+ // CID: post.CID,
+ // Collection: "app.bsky.feed.post",
+ // }})
+ // time.Sleep(time.Second * 2)
+ // }
+ // return
+ // ------------------------------------------------------------------------------
+
cursor := time.Now().UnixMicro()
restartCount := 0
loop:
@@ -164,19 +145,19 @@ func (h *handler) HandleEvent(ctx context.Context, event *models.Event) error {
return nil
}
- switch event.Commit.Operation {
- case models.CommitOperationCreate, models.CommitOperationUpdate:
+ if event.Commit.Operation == models.CommitOperationCreate ||
+ event.Commit.Operation == models.CommitOperationUpdate {
h.bsky.Bluesky.Cfg.Cursor = event.TimeUS + 1 // +1 to not show same post
bsky.PersistAuthSession(h.bsky.Bluesky.Cfg)
h.ProcessPost(event)
- case models.CommitOperationDelete:
+ } else if event.Commit.Operation == models.CommitOperationDelete {
h.bsky.Bluesky.Cfg.Cursor = event.TimeUS + 1 // +1 to not show same post
bsky.PersistAuthSession(h.bsky.Bluesky.Cfg)
r, e := h.bsky.Bluesky.GetTelegramData(event.Commit.RKey)
if e == "" {
- m := tgbotapi.NewDeleteMessages(r.ChannelID, r.MessageID)
+ m := tgbotapi.NewDeleteMessage(r.ChannelID, r.MessageID)
h.tg.Send(m)
- h.bsky.Bluesky.DeleteRecord([]string{event.Commit.RKey, event.Did, "blue.zio.bsky2tg.post"})
+ h.bsky.Bluesky.DeleteRecord([]string{event.Commit.RKey, event.Did, event.Commit.Collection})
}
}
@@ -283,13 +264,9 @@ func (h *handler) ProcessPost(event *models.Event) error {
} else {
resp, _ := h.tg.SendMediaGroup(tgbotapi.NewMediaGroup(cid, mediaGroup))
uri, cid := getLink(event)
- var messageIDs []int
- for _, msgID := range resp {
- messageIDs = append(messageIDs, msgID.MessageID)
- }
h.bsky.Bluesky.CommitTelegramResponse(&bsky.TelegramRecord{
ChannelID: resp[0].Chat.ID,
- MessageID: messageIDs,
+ MessageID: resp[0].MessageID,
Link: &bsky.Link{
Cid: cid,
URI: uri,
@@ -307,11 +284,10 @@ func (h *handler) ProcessPost(event *models.Event) error {
if ps.IsQuotePost() {
m.LinkPreviewOptions = tgbotapi.LinkPreviewOptions{
IsDisabled: false,
- URL: fmt.Sprintf("https://fxbsky.app/profile/%s/post/%s",
+ URL: fmt.Sprintf("https://bsky.app/profile/%s/post/%s",
strings.Split(ps.Embed.Record.URI, "/")[2],
strings.Split(ps.Embed.Record.URI, "/")[4]),
- PreferSmallMedia: false,
- PreferLargeMedia: true,
+ PreferSmallMedia: true,
ShowAboveText: true,
}
} else {
@@ -321,7 +297,7 @@ func (h *handler) ProcessPost(event *models.Event) error {
uri, cid := getLink(event)
h.bsky.Bluesky.CommitTelegramResponse(&bsky.TelegramRecord{
ChannelID: resp.Chat.ID,
- MessageID: []int{resp.MessageID},
+ MessageID: resp.MessageID,
Link: &bsky.Link{
Cid: cid,
URI: uri,
@@ -332,7 +308,7 @@ func (h *handler) ProcessPost(event *models.Event) error {
}
func buildBlobURL(server string, did string, cid string) string {
- return server + "/xrpc/com.atproto.sync.getBlob?did=" + url.QueryEscape(did) + "&cid=" + cid
+ return server + "/xrpc/com.atproto.sync.getBlob?did=" + url.QueryEscape(did) + "&cid=" + url.QueryEscape(cid)
}
func getLink(event *models.Event) (string, string) {