Handle recordWithMedia, refactor processPost function
All checks were successful
/ build (push) Successful in 2m20s

This commit is contained in:
Astra 2026-03-26 07:49:58 +00:00
parent e2faeaac75
commit e8b6e04239
3 changed files with 249 additions and 117 deletions

View file

@ -294,6 +294,6 @@ func (bluesky *Bluesky) FetchPost(did string, rkey string) FetchedPost {
}{ }{
URIs: fmt.Sprintf("at://%s/app.bsky.feed.post/%s", did, rkey), URIs: fmt.Sprintf("at://%s/app.bsky.feed.post/%s", did, rkey),
} }
bluesky.sling.New().Get("/xrpc/app.bsky.feed.getPosts").QueryStruct(&params).Receive(resp, resp) bluesky.publicSling.New().Get("/xrpc/app.bsky.feed.getPosts").QueryStruct(&params).Receive(resp, resp)
return resp.Posts[0] return resp.Posts[0]
} }

View file

@ -16,7 +16,7 @@ type Post struct {
Langs []string `json:"langs,omitempty"` Langs []string `json:"langs,omitempty"`
Labels *Labels `json:"labels,omitempty"` Labels *Labels `json:"labels,omitempty"`
Reply *Reply `json:"reply,omitempty"` Reply *Reply `json:"reply,omitempty"`
Facets *[]Facets `json:"facets,omitempty"` Facets []Facets `json:"facets,omitempty"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
} }
@ -67,7 +67,7 @@ type Media struct {
Type string `json:"$type,omitempty"` Type string `json:"$type,omitempty"`
External *External `json:"external,omitempty"` External *External `json:"external,omitempty"`
Video *Video `json:"video,omitempty"` Video *Video `json:"video,omitempty"`
Images *[]Images `json:"images,omitempty"` Images []Images `json:"images,omitempty"`
AspectRatio *AspectRatio `json:"aspectRatio,omitempty"` AspectRatio *AspectRatio `json:"aspectRatio,omitempty"`
} }
@ -86,7 +86,7 @@ type PostRecord struct {
type Embed struct { type Embed struct {
Type string `json:"$type,omitempty"` Type string `json:"$type,omitempty"`
Media *Media `json:"media,omitempty"` Media *Media `json:"media,omitempty"`
Images *[]Images `json:"images,omitempty"` Images []Images `json:"images,omitempty"`
Video *Video `json:"video,omitempty"` Video *Video `json:"video,omitempty"`
Record *PostRecord `json:"record,omitempty"` Record *PostRecord `json:"record,omitempty"`
External *External `json:"external,omitempty"` External *External `json:"external,omitempty"`
@ -97,8 +97,8 @@ type Values struct {
} }
type Labels struct { type Labels struct {
Type string `json:"$type,omitempty"` Type string `json:"$type,omitempty"`
Values *[]Values `json:"values,omitempty"` Values []Values `json:"values,omitempty"`
} }
type Root struct { type Root struct {
@ -145,9 +145,9 @@ type Features struct {
} }
type Facets struct { type Facets struct {
Type string `json:"$type"` Type string `json:"$type"`
Index *Index `json:"index,omitempty"` Index *Index `json:"index,omitempty"`
Features *[]Features `json:"features,omitempty"` Features []Features `json:"features,omitempty"`
} }
type ParsedEmbeds struct { type ParsedEmbeds struct {
@ -333,33 +333,36 @@ func (post *Post) ProcessFacets(aliases []Records) string {
return html.EscapeString(post.Text) return html.EscapeString(post.Text)
} }
sort.Slice((*post.Facets), func(i, j int) bool { sort.Slice((post.Facets), func(i, j int) bool {
return (*post.Facets)[i].Index.ByteStart < (*post.Facets)[j].Index.ByteStart return (post.Facets)[i].Index.ByteStart < (post.Facets)[j].Index.ByteStart
}) })
var result strings.Builder var result strings.Builder
lastIndex := 0 lastIndex := 0
for _, facet := range *post.Facets { for _, facet := range post.Facets {
start := facet.Index.ByteStart start := facet.Index.ByteStart
end := facet.Index.ByteEnd end := facet.Index.ByteEnd
// Escape HTML in plain text portions // Escape HTML in plain text portions
result.WriteString(html.EscapeString(post.Text[lastIndex:start])) result.WriteString(html.EscapeString(post.Text[lastIndex:start]))
for _, feature := range *facet.Features { for _, feature := range facet.Features {
switch feature.Type { switch feature.Type {
case "app.bsky.richtext.facet#mention": case "app.bsky.richtext.facet#mention":
link := fmt.Sprintf(`<a href="https://bsky.app/profile/%s">%s</a>`, feature.Did, html.EscapeString(post.Text[start:end])) link := fmt.Sprintf(`<a href="https://bsky.app/profile/%s">%s</a>`, feature.Did, html.EscapeString(post.Text[start:end]))
for _, alias := range aliases { for _, alias := range aliases {
if alias.Value.Subject == feature.Did { if alias.Value.Subject == feature.Did {
link = fmt.Sprintf(`<a href="%s">%s</a>`, parts := strings.SplitN(alias.Value.Target, "#", 2)
strings.SplitN(alias.Value.Target, "#", 2)[0], strings.SplitN(alias.Value.Target, "#", 2)[1]) if len(parts) == 2 {
link = fmt.Sprintf(`<a href="%s">%s</a>`, parts[0], parts[1])
}
} }
} }
result.WriteString(link) result.WriteString(link)
case "app.bsky.richtext.facet#link": case "app.bsky.richtext.facet#link":
link := fmt.Sprintf(`<a href="%s">%s</a>`, feature.URI, html.EscapeString(post.Text[start:end])) uri := strings.Trim(feature.URI, "\"")
link := fmt.Sprintf(`<a href="%s">%s</a>`, uri, html.EscapeString(post.Text[start:end]))
result.WriteString(link) result.WriteString(link)
case "app.bsky.richtext.facet#tag": case "app.bsky.richtext.facet#tag":
link := fmt.Sprintf(`<a href="https://bsky.app/hashtag/%s">%s</a>`, feature.Tag, html.EscapeString(post.Text[start:end])) link := fmt.Sprintf(`<a href="https://bsky.app/hashtag/%s">%s</a>`, feature.Tag, html.EscapeString(post.Text[start:end]))
@ -375,60 +378,88 @@ func (post *Post) ProcessFacets(aliases []Records) string {
return result.String() return result.String()
} }
func (p *Post) GetEmbeds() *[]ParsedEmbeds { func (p *Post) GetEmbeds() []ParsedEmbeds {
var parsedEmbeds = &[]ParsedEmbeds{} var parsedEmbeds []ParsedEmbeds
if p.Embed != nil {
if p.Embed.Video != nil { if p.Embed == nil {
parsedEmbed := ParsedEmbeds{ return parsedEmbeds
}
switch p.Embed.Type {
case "app.bsky.embed.images":
for _, image := range p.Embed.Images {
if image.Image != nil && image.Image.Ref != nil {
parsedEmbeds = append(parsedEmbeds, ParsedEmbeds{
URI: image.Image.Ref.Link,
Type: "image",
})
}
}
case "app.bsky.embed.video":
if p.Embed.Video != nil && p.Embed.Video.Ref != nil {
parsedEmbeds = append(parsedEmbeds, ParsedEmbeds{
URI: p.Embed.Video.Ref.Link, URI: p.Embed.Video.Ref.Link,
Type: "video", Type: "video",
} })
*parsedEmbeds = append(*parsedEmbeds, parsedEmbed)
} }
case "app.bsky.embed.external":
if p.Embed.External != nil { if p.Embed.External != nil {
if strings.Contains(p.Embed.External.URI, "media.tenor.com") { t := "external"
parsedEmbed := ParsedEmbeds{ if strings.Contains(p.Embed.External.URI, "tenor.com") {
URI: p.Embed.External.URI, t = "gif"
Type: "external",
}
*parsedEmbeds = append(*parsedEmbeds, parsedEmbed)
} }
parsedEmbeds = append(parsedEmbeds, ParsedEmbeds{
URI: p.Embed.External.URI,
Type: t,
})
}
case "app.bsky.embed.record":
if p.Embed.Record != nil {
parsedEmbeds = append(parsedEmbeds, ParsedEmbeds{
URI: p.Embed.Record.Record.URI,
Cid: p.Embed.Record.Record.Cid,
Type: "record",
})
}
case "app.bsky.embed.recordWithMedia":
// Quote post - also extract the media it contains
if p.Embed.Record != nil {
parsedEmbeds = append(parsedEmbeds, ParsedEmbeds{
URI: p.Embed.Record.Record.URI,
Cid: p.Embed.Record.Record.Cid,
Type: "record",
})
} }
if p.Embed.Media != nil { if p.Embed.Media != nil {
if p.Embed.Media.Images != nil { if p.Embed.Media.Images != nil {
for _, image := range *p.Embed.Media.Images { for _, image := range p.Embed.Media.Images {
parsedEmbed := ParsedEmbeds{ if image.Image != nil && image.Image.Ref != nil {
URI: image.Image.Ref.Link, parsedEmbeds = append(parsedEmbeds, ParsedEmbeds{
Type: "image", URI: image.Image.Ref.Link,
Type: "image",
})
} }
*parsedEmbeds = append(*parsedEmbeds, parsedEmbed)
} }
} }
if p.Embed.Media.Video != nil { if p.Embed.Media.Video != nil && p.Embed.Media.Video.Ref != nil {
parsedEmbed := ParsedEmbeds{ parsedEmbeds = append(parsedEmbeds, ParsedEmbeds{
URI: p.Embed.Media.Video.Ref.Link, URI: p.Embed.Media.Video.Ref.Link,
Type: "video", Type: "video",
} })
*parsedEmbeds = append(*parsedEmbeds, parsedEmbed)
} }
if p.Embed.Media.External != nil { if p.Embed.Media.External != nil {
parsedEmbed := ParsedEmbeds{ parsedEmbeds = append(parsedEmbeds, ParsedEmbeds{
URI: p.Embed.Media.External.URI, URI: p.Embed.Media.External.URI,
Type: "external", Type: "external",
} })
*parsedEmbeds = append(*parsedEmbeds, parsedEmbed)
}
}
if p.Embed.Images != nil {
for _, image := range *p.Embed.Images {
parsedEmbed := ParsedEmbeds{
URI: image.Image.Ref.Link,
Type: "image",
}
*parsedEmbeds = append(*parsedEmbeds, parsedEmbed)
} }
} }
} }
return parsedEmbeds return parsedEmbeds
} }
@ -441,7 +472,7 @@ func (p *Post) GetMedia() *Media {
return nil return nil
} }
func (p *Post) GetMediaImages() *[]Images { func (p *Post) GetMediaImages() []Images {
if p.GetMedia() != nil { if p.GetMedia() != nil {
return p.GetMedia().Images return p.GetMedia().Images
} }

233
main.go
View file

@ -186,6 +186,53 @@ func (h *handler) HandleEvent(ctx context.Context, event *models.Event) error {
return nil return nil
} }
func (h *handler) handleVideo(media bsky.ParsedEmbeds) (tgbotapi.InputMedia, error) {
url := buildBlobURL(h.bsky.Bluesky.Cfg.PDSURL, h.bsky.Bluesky.Cfg.DID, media.URI)
log.Printf("Fetching video: %s\n", url)
resp, err := http.Get(url)
if err != nil {
return nil, fmt.Errorf("failed to fetch video: %w", err)
}
defer resp.Body.Close()
filename := media.URI + ".mp4"
f, err := os.Create(filename)
if err != nil {
return nil, fmt.Errorf("failed to create video file: %w", err)
}
defer func() {
f.Close()
os.Remove(filename)
}()
if _, err := io.Copy(f, resp.Body); err != nil {
return nil, fmt.Errorf("failed to write video: %w", err)
}
if _, err := f.Seek(0, 0); err != nil {
return nil, fmt.Errorf("failed to seek video: %w", err)
}
mediaAdd := tgbotapi.NewInputMediaVideo(tgbotapi.FileReader{Name: "video.mp4", Reader: f})
metadata, err := getVideoMetadata(f.Name())
if err != nil {
return nil, fmt.Errorf("failed to read video metadata: %w", err)
}
mediaAdd.SupportsStreaming = true
mediaAdd.Height = metadata.Height()
mediaAdd.Width = metadata.Width()
mediaAdd.Duration = int(metadata.Duration())
frames, _ := metadata.ReadFrames(0)
var buf bytes.Buffer
jpeg.Encode(&buf, frames[0], &jpeg.Options{Quality: 90})
mediaAdd.Thumb = tgbotapi.FileBytes{Name: "thumb.jpg", Bytes: buf.Bytes()}
return &mediaAdd, nil
}
func (h *handler) ProcessPost(event *models.Event) error { func (h *handler) ProcessPost(event *models.Event) error {
ps, _ := h.bsky.ParsePost(event.Commit.Record) ps, _ := h.bsky.ParsePost(event.Commit.Record)
po := ps.GetEmbeds() po := ps.GetEmbeds()
@ -209,42 +256,38 @@ func (h *handler) ProcessPost(event *models.Event) error {
aliases := h.bsky.Bluesky.FetchAliases() aliases := h.bsky.Bluesky.FetchAliases()
facets := ps.ProcessFacets(aliases) facets := ps.ProcessFacets(aliases)
ownHandle, handleErr := h.bsky.GetHandleFromDID(h.bsky.Bluesky.Cfg.DID)
if handleErr != nil {
ownHandle = h.bsky.Bluesky.Cfg.Handle
}
var captionText string var captionText string
if ps.IsQuotePost() { if ps.IsQuotePost() {
ownHandle, handleErr := h.bsky.GetHandleFromDID(h.bsky.Bluesky.Cfg.DID) var quotedURI string
if handleErr != nil { if ps.Embed.Record != nil && ps.Embed.Record.Record != nil && ps.Embed.Record.Record.URI != "" {
ownHandle = h.bsky.Bluesky.Cfg.Handle quotedURI = ps.Embed.Record.Record.URI
} else if ps.Embed.Record != nil && ps.Embed.Record.URI != "" {
quotedURI = ps.Embed.Record.URI
} }
if ps.Embed.Record.Type == "app.bsky.embed.record" {
handle, _ := h.bsky.GetHandleFromDID(strings.Split(ps.Embed.Record.Record.URI, "/")[2]) if quotedURI != "" {
captionText = fmt.Sprintf( parts := strings.Split(quotedURI, "/")
quotePostFormat, if len(parts) >= 5 {
facets, handle, _ := h.bsky.GetHandleFromDID(parts[2])
strings.Split(ps.Embed.Record.Record.URI, "/")[2], captionText = fmt.Sprintf(
strings.Split(ps.Embed.Record.Record.URI, "/")[4], quotePostFormat,
handle, facets,
event.Did, parts[2],
event.Commit.RKey, parts[4],
ownHandle) handle,
} else { event.Did,
handle, _ := h.bsky.GetHandleFromDID(strings.Split(ps.Embed.Record.URI, "/")[2]) event.Commit.RKey,
captionText = fmt.Sprintf( ownHandle)
quotePostFormat, }
facets,
strings.Split(ps.Embed.Record.URI, "/")[2],
strings.Split(ps.Embed.Record.URI, "/")[4],
handle,
event.Did,
event.Commit.RKey,
ownHandle)
} }
} }
if captionText == "" { if captionText == "" {
ownHandle, handleErr := h.bsky.GetHandleFromDID(h.bsky.Bluesky.Cfg.DID)
if handleErr != nil {
ownHandle = h.bsky.Bluesky.Cfg.Handle
}
if facets != "" { if facets != "" {
captionText = fmt.Sprintf(postFormat, facets, h.bsky.Bluesky.Cfg.DID, event.Commit.RKey, ownHandle) captionText = fmt.Sprintf(postFormat, facets, h.bsky.Bluesky.Cfg.DID, event.Commit.RKey, ownHandle)
} else { } else {
@ -252,16 +295,72 @@ func (h *handler) ProcessPost(event *models.Event) error {
} }
} }
// post has media if len(po) != 0 {
if len((*po)) != 0 {
mediaGroup := []tgbotapi.InputMedia{} mediaGroup := []tgbotapi.InputMedia{}
if (*po)[0].Type == "external" {
tenorGif := tgbotapi.NewInputMediaVideo(tgbotapi.FileURL((*po)[0].URI)) // is most likely gif from Tenor if ps.Embed.Type == "app.bsky.embed.recordWithMedia" {
hasExternal := false
for _, media := range po {
if media.Type == "external" {
hasExternal = true
break
}
}
if hasExternal && ps.Embed.Media != nil && ps.Embed.Media.External != nil {
// Send as text message with webpage preview
m := tgbotapi.NewMessage(cid, captionText)
m.ParseMode = tgbotapi.ModeHTML
m.LinkPreviewOptions = tgbotapi.LinkPreviewOptions{
IsDisabled: false,
URL: ps.Embed.Media.External.URI,
PreferLargeMedia: true,
ShowAboveText: false,
}
resp, _ := h.tg.Send(m)
uri, postCid := getLink(event)
h.bsky.Bluesky.CommitTelegramResponse(&bsky.TelegramRecord{
ChannelID: resp.Chat.ID,
MessageID: []int{resp.MessageID},
Link: &bsky.Link{
Cid: postCid,
URI: uri,
},
}, event.Commit.RKey)
return nil
}
// recordWithMedia with images or video (not external) — fall through to normal media handling
for _, media := range po {
switch media.Type {
case "record":
continue
case "image":
mediaAdd := tgbotapi.NewInputMediaPhoto(tgbotapi.FileURL(buildBlobURL(h.bsky.Bluesky.Cfg.PDSURL, h.bsky.Bluesky.Cfg.DID, media.URI)))
if len(mediaGroup) == 0 {
mediaAdd.Caption = captionText
mediaAdd.ParseMode = tgbotapi.ModeHTML
}
mediaGroup = append(mediaGroup, &mediaAdd)
case "video":
mediaAdd, err := h.handleVideo(media)
if err != nil {
log.Printf("Failed to handle video: %s\n", err)
break
}
if len(mediaGroup) == 0 {
setCaption(mediaAdd, captionText)
}
mediaGroup = append(mediaGroup, mediaAdd)
}
}
} else if po[0].Type == "external" {
tenorGif := tgbotapi.NewInputMediaVideo(tgbotapi.FileURL(po[0].URI))
tenorGif.Caption = captionText tenorGif.Caption = captionText
tenorGif.ParseMode = tgbotapi.ModeHTML tenorGif.ParseMode = tgbotapi.ModeHTML
mediaGroup = append(mediaGroup, &tenorGif) mediaGroup = append(mediaGroup, &tenorGif)
} else { } else {
for _, media := range *po { for _, media := range po {
switch media.Type { switch media.Type {
case "image": case "image":
mediaAdd := tgbotapi.NewInputMediaPhoto(tgbotapi.FileURL(buildBlobURL(h.bsky.Bluesky.Cfg.PDSURL, h.bsky.Bluesky.Cfg.DID, media.URI))) mediaAdd := tgbotapi.NewInputMediaPhoto(tgbotapi.FileURL(buildBlobURL(h.bsky.Bluesky.Cfg.PDSURL, h.bsky.Bluesky.Cfg.DID, media.URI)))
@ -271,37 +370,19 @@ func (h *handler) ProcessPost(event *models.Event) error {
} }
mediaGroup = append(mediaGroup, &mediaAdd) mediaGroup = append(mediaGroup, &mediaAdd)
case "video": case "video":
log.Printf("Fetching video: %s\n", buildBlobURL(h.bsky.Bluesky.Cfg.PDSURL, h.bsky.Bluesky.Cfg.DID, media.URI)) mediaAdd, err := h.handleVideo(media)
resp, _ := http.Get(buildBlobURL(h.bsky.Bluesky.Cfg.PDSURL, h.bsky.Bluesky.Cfg.DID, media.URI))
defer resp.Body.Close()
f, _ := os.Create(media.URI + ".mp4")
defer f.Close()
io.Copy(f, resp.Body)
f.Seek(0, 0)
mediaAdd := tgbotapi.NewInputMediaVideo(tgbotapi.FileReader{Name: "video.mp4", Reader: f})
metadata, err := getVideoMetadata(f.Name())
if err != nil { if err != nil {
log.Printf("Unable to read video metadata: %s - URL: %s\n", err, buildBlobURL(h.bsky.Bluesky.Cfg.PDSURL, h.bsky.Bluesky.Cfg.DID, media.URI)) log.Printf("Failed to handle video: %s\n", err)
break break
} }
mediaAdd.SupportsStreaming = true
mediaAdd.Height = metadata.Height()
mediaAdd.Width = metadata.Width()
mediaAdd.Duration = int(metadata.Duration())
frames, _ := metadata.ReadFrames(0)
var buf bytes.Buffer
jpeg.Encode(&buf, frames[0], &jpeg.Options{Quality: 90})
mediaAdd.Thumb = tgbotapi.FileBytes{Name: "thumb.jpg", Bytes: buf.Bytes()}
if len(mediaGroup) == 0 { if len(mediaGroup) == 0 {
mediaAdd.Caption = captionText setCaption(mediaAdd, captionText)
mediaAdd.ParseMode = tgbotapi.ModeHTML
} }
os.Remove(media.URI + ".mp4") mediaGroup = append(mediaGroup, mediaAdd)
mediaGroup = append(mediaGroup, &mediaAdd)
} }
} }
} }
if len(mediaGroup) == 0 { if len(mediaGroup) == 0 {
log.Print("No mediaGroup to send, see previous error") log.Print("No mediaGroup to send, see previous error")
} else { } else {
@ -310,7 +391,7 @@ func (h *handler) ProcessPost(event *models.Event) error {
fmt.Println(resp, err) fmt.Println(resp, err)
} else { } else {
resp, _ := h.tg.SendMediaGroup(tgbotapi.NewMediaGroup(cid, mediaGroup)) resp, _ := h.tg.SendMediaGroup(tgbotapi.NewMediaGroup(cid, mediaGroup))
uri, cid := getLink(event) uri, postCid := getLink(event)
var messageIDs []int var messageIDs []int
for _, msgID := range resp { for _, msgID := range resp {
messageIDs = append(messageIDs, msgID.MessageID) messageIDs = append(messageIDs, msgID.MessageID)
@ -319,7 +400,7 @@ func (h *handler) ProcessPost(event *models.Event) error {
ChannelID: resp[0].Chat.ID, ChannelID: resp[0].Chat.ID,
MessageID: messageIDs, MessageID: messageIDs,
Link: &bsky.Link{ Link: &bsky.Link{
Cid: cid, Cid: postCid,
URI: uri, URI: uri,
}, },
}, event.Commit.RKey) }, event.Commit.RKey)
@ -334,11 +415,19 @@ func (h *handler) ProcessPost(event *models.Event) error {
} }
m.ParseMode = tgbotapi.ModeHTML m.ParseMode = tgbotapi.ModeHTML
if ps.IsQuotePost() { if ps.IsQuotePost() {
m.LinkPreviewOptions = tgbotapi.LinkPreviewOptions{ var previewURI string
IsDisabled: false, if ps.Embed.Record != nil && ps.Embed.Record.Record != nil {
URL: fmt.Sprintf(embedURL, previewURI = fmt.Sprintf(embedURL,
strings.Split(ps.Embed.Record.Record.URI, "/")[2],
strings.Split(ps.Embed.Record.Record.URI, "/")[4])
} else if ps.Embed.Record != nil {
previewURI = fmt.Sprintf(embedURL,
strings.Split(ps.Embed.Record.URI, "/")[2], strings.Split(ps.Embed.Record.URI, "/")[2],
strings.Split(ps.Embed.Record.URI, "/")[4]), strings.Split(ps.Embed.Record.URI, "/")[4])
}
m.LinkPreviewOptions = tgbotapi.LinkPreviewOptions{
IsDisabled: false,
URL: previewURI,
PreferSmallMedia: true, PreferSmallMedia: true,
PreferLargeMedia: false, PreferLargeMedia: false,
ShowAboveText: true, ShowAboveText: true,
@ -346,13 +435,14 @@ func (h *handler) ProcessPost(event *models.Event) error {
} else { } else {
m.LinkPreviewOptions = tgbotapi.LinkPreviewOptions{IsDisabled: true} m.LinkPreviewOptions = tgbotapi.LinkPreviewOptions{IsDisabled: true}
} }
resp, _ := h.tg.Send(m) resp, e := h.tg.Send(m)
uri, cid := getLink(event) fmt.Println(resp, e)
uri, postCid := getLink(event)
h.bsky.Bluesky.CommitTelegramResponse(&bsky.TelegramRecord{ h.bsky.Bluesky.CommitTelegramResponse(&bsky.TelegramRecord{
ChannelID: resp.Chat.ID, ChannelID: resp.Chat.ID,
MessageID: []int{resp.MessageID}, MessageID: []int{resp.MessageID},
Link: &bsky.Link{ Link: &bsky.Link{
Cid: cid, Cid: postCid,
URI: uri, URI: uri,
}, },
}, event.Commit.RKey) }, event.Commit.RKey)
@ -360,6 +450,17 @@ func (h *handler) ProcessPost(event *models.Event) error {
return nil return nil
} }
func setCaption(media tgbotapi.InputMedia, caption string) {
switch m := media.(type) {
case *tgbotapi.InputMediaVideo:
m.Caption = caption
m.ParseMode = tgbotapi.ModeHTML
case *tgbotapi.InputMediaPhoto:
m.Caption = caption
m.ParseMode = tgbotapi.ModeHTML
}
}
func buildBlobURL(server string, did string, cid string) string { 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=" + cid
} }