Handle recordWithMedia, refactor processPost function
All checks were successful
/ build (push) Successful in 2m20s
All checks were successful
/ build (push) Successful in 2m20s
This commit is contained in:
parent
e2faeaac75
commit
e8b6e04239
3 changed files with 249 additions and 117 deletions
|
|
@ -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(¶ms).Receive(resp, resp)
|
bluesky.publicSling.New().Get("/xrpc/app.bsky.feed.getPosts").QueryStruct(¶ms).Receive(resp, resp)
|
||||||
return resp.Posts[0]
|
return resp.Posts[0]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
119
bsky/parse.go
119
bsky/parse.go
|
|
@ -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"`
|
||||||
|
|
@ -98,7 +98,7 @@ 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 {
|
||||||
|
|
@ -147,7 +147,7 @@ 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") {
|
||||||
|
t = "gif"
|
||||||
|
}
|
||||||
|
parsedEmbeds = append(parsedEmbeds, ParsedEmbeds{
|
||||||
URI: p.Embed.External.URI,
|
URI: p.Embed.External.URI,
|
||||||
Type: "external",
|
Type: t,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
*parsedEmbeds = append(*parsedEmbeds, parsedEmbed)
|
|
||||||
|
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 {
|
||||||
|
parsedEmbeds = append(parsedEmbeds, ParsedEmbeds{
|
||||||
URI: image.Image.Ref.Link,
|
URI: image.Image.Ref.Link,
|
||||||
Type: "image",
|
Type: "image",
|
||||||
}
|
})
|
||||||
*parsedEmbeds = append(*parsedEmbeds, parsedEmbed)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p.Embed.Media.Video != nil {
|
}
|
||||||
parsedEmbed := ParsedEmbeds{
|
if p.Embed.Media.Video != nil && p.Embed.Media.Video.Ref != nil {
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
215
main.go
215
main.go
|
|
@ -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)
|
||||||
|
|
||||||
var captionText string
|
|
||||||
if ps.IsQuotePost() {
|
|
||||||
ownHandle, handleErr := h.bsky.GetHandleFromDID(h.bsky.Bluesky.Cfg.DID)
|
ownHandle, handleErr := h.bsky.GetHandleFromDID(h.bsky.Bluesky.Cfg.DID)
|
||||||
if handleErr != nil {
|
if handleErr != nil {
|
||||||
ownHandle = h.bsky.Bluesky.Cfg.Handle
|
ownHandle = h.bsky.Bluesky.Cfg.Handle
|
||||||
}
|
}
|
||||||
if ps.Embed.Record.Type == "app.bsky.embed.record" {
|
|
||||||
handle, _ := h.bsky.GetHandleFromDID(strings.Split(ps.Embed.Record.Record.URI, "/")[2])
|
var captionText string
|
||||||
|
if ps.IsQuotePost() {
|
||||||
|
var quotedURI string
|
||||||
|
if ps.Embed.Record != nil && ps.Embed.Record.Record != nil && ps.Embed.Record.Record.URI != "" {
|
||||||
|
quotedURI = ps.Embed.Record.Record.URI
|
||||||
|
} else if ps.Embed.Record != nil && ps.Embed.Record.URI != "" {
|
||||||
|
quotedURI = ps.Embed.Record.URI
|
||||||
|
}
|
||||||
|
|
||||||
|
if quotedURI != "" {
|
||||||
|
parts := strings.Split(quotedURI, "/")
|
||||||
|
if len(parts) >= 5 {
|
||||||
|
handle, _ := h.bsky.GetHandleFromDID(parts[2])
|
||||||
captionText = fmt.Sprintf(
|
captionText = fmt.Sprintf(
|
||||||
quotePostFormat,
|
quotePostFormat,
|
||||||
facets,
|
facets,
|
||||||
strings.Split(ps.Embed.Record.Record.URI, "/")[2],
|
parts[2],
|
||||||
strings.Split(ps.Embed.Record.Record.URI, "/")[4],
|
parts[4],
|
||||||
handle,
|
|
||||||
event.Did,
|
|
||||||
event.Commit.RKey,
|
|
||||||
ownHandle)
|
|
||||||
} else {
|
|
||||||
handle, _ := h.bsky.GetHandleFromDID(strings.Split(ps.Embed.Record.URI, "/")[2])
|
|
||||||
captionText = fmt.Sprintf(
|
|
||||||
quotePostFormat,
|
|
||||||
facets,
|
|
||||||
strings.Split(ps.Embed.Record.URI, "/")[2],
|
|
||||||
strings.Split(ps.Embed.Record.URI, "/")[4],
|
|
||||||
handle,
|
handle,
|
||||||
event.Did,
|
event.Did,
|
||||||
event.Commit.RKey,
|
event.Commit.RKey,
|
||||||
ownHandle)
|
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() {
|
||||||
|
var previewURI string
|
||||||
|
if ps.Embed.Record != nil && ps.Embed.Record.Record != nil {
|
||||||
|
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, "/")[4])
|
||||||
|
}
|
||||||
m.LinkPreviewOptions = tgbotapi.LinkPreviewOptions{
|
m.LinkPreviewOptions = tgbotapi.LinkPreviewOptions{
|
||||||
IsDisabled: false,
|
IsDisabled: false,
|
||||||
URL: fmt.Sprintf(embedURL,
|
URL: previewURI,
|
||||||
strings.Split(ps.Embed.Record.URI, "/")[2],
|
|
||||||
strings.Split(ps.Embed.Record.URI, "/")[4]),
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue