naco-api/internal/server/posts_convert.go
Nacorid 2fd4a4ec90
refactor: finish activitypub post migration from Text to Content
Drop the redundant Post.Text/internalPost.Text fields and complete the
rename in server converters and test fixtures so the tree builds again.

- types.go: remove Text from Post and internalPost; toPost only writes
  Author when the account acct is present
- posts_convert.go: apPostsPtrToItems / apPostsToAnalyticsItems read Content
- server tests: drop duplicate Text literal fields (values identical to Content)
2026-09-09 23:17:35 +00:00

255 lines
6.4 KiB
Go

package server
import (
"time"
"github.com/bluesky-social/indigo/api/bsky"
"github.com/nacorid/naco-api/internal/activitypub"
"github.com/nacorid/naco-api/internal/clustering"
"github.com/nacorid/naco-api/internal/nostr"
"github.com/nacorid/naco-api/internal/twitter"
)
func bskyPostViewToResponse(p *bsky.FeedDefs_PostView) *GetPostResponsePost {
author := ""
if p.Author != nil {
author = p.Author.Did
}
content := ""
created := ""
if fp, ok := p.Record.Val.(*bsky.FeedPost); ok {
content = fp.Text
created = fp.CreatedAt
}
likes, reposts, quotes, replies := int64(0), int64(0), int64(0), int64(0)
if p.LikeCount != nil {
likes = *p.LikeCount
}
if p.RepostCount != nil {
reposts = *p.RepostCount
}
if p.QuoteCount != nil {
quotes = *p.QuoteCount
}
if p.ReplyCount != nil {
replies = *p.ReplyCount
}
return &GetPostResponsePost{
Author: author,
Content: content,
Likes: likes,
Reposts: reposts,
Quotes: quotes,
Replies: replies,
CreatedAt: created,
}
}
func apPostToResponse(p *activitypub.Post) *GetPostResponsePost {
created := ""
if !p.CreatedAt.IsZero() {
created = p.CreatedAt.Format(time.RFC3339)
}
return &GetPostResponsePost{
Author: p.Author,
Content: p.Content,
Likes: int64(p.FavouritesCount),
Reposts: int64(p.ReblogsCount),
Replies: int64(p.RepliesCount),
CreatedAt: created,
}
}
func nostrPostToResponse(p *nostr.Post) *GetPostResponsePost {
created := ""
if !p.CreatedAt.IsZero() {
created = p.CreatedAt.Format(time.RFC3339)
}
return &GetPostResponsePost{
Author: p.Author,
Content: p.Content,
CreatedAt: created,
}
}
func twitterPostToResponse(p *twitter.Post) *GetPostResponsePost {
created := ""
if !p.CreatedAt.IsZero() {
created = p.CreatedAt.Format(time.RFC3339)
}
return &GetPostResponsePost{
Author: p.Author,
Content: p.Content,
Likes: int64(p.Likes),
Reposts: int64(p.Reposts),
Quotes: int64(p.Quotes),
Replies: int64(p.Replies),
CreatedAt: created,
}
}
// ---- Batch converters for GetPostResponsePost ----
func blueskyFeedViewsToResponses(views []*bsky.FeedDefs_FeedViewPost) ([]*GetPostResponsePost, string) {
posts := make([]*GetPostResponsePost, 0, len(views))
var aid string
for _, p := range views {
if p.Post == nil {
continue
}
posts = append(posts, bskyPostViewToResponse(p.Post))
if p.Post.Author != nil {
aid = p.Post.Author.Did
}
}
return posts, aid
}
func apPostsPtrToResponses(posts []*activitypub.Post) ([]*GetPostResponsePost, string) {
out := make([]*GetPostResponsePost, 0, len(posts))
var aid string
for _, p := range posts {
out = append(out, apPostToResponse(p))
aid = p.Author
}
return out, aid
}
func apPostsToResponses(posts []*activitypub.Post) ([]*GetPostResponsePost, string) {
out := make([]*GetPostResponsePost, 0, len(posts))
var aid string
for _, p := range posts {
out = append(out, apPostToResponse(p))
aid = p.Author
}
return out, aid
}
func nostrPostsToResponses(posts []*nostr.Post) ([]*GetPostResponsePost, string) {
out := make([]*GetPostResponsePost, 0, len(posts))
var aid string
for _, p := range posts {
out = append(out, nostrPostToResponse(p))
aid = p.Author
}
return out, aid
}
func twitterPostsToResponses(posts []*twitter.Post) ([]*GetPostResponsePost, string) {
out := make([]*GetPostResponsePost, 0, len(posts))
var aid string
for _, p := range posts {
out = append(out, twitterPostToResponse(p))
aid = p.Author
}
return out, aid
}
// ---- Batch converters for clustering.Item (style & analytics) ----
func blueskyFeedViewsToItems(views []*bsky.FeedDefs_FeedViewPost) []clustering.Item {
out := make([]clustering.Item, 0, len(views))
for _, p := range views {
fp, ok := p.Post.Record.Val.(*bsky.FeedPost)
if !ok {
continue
}
if fp.Text == "" {
continue
}
out = append(out, clustering.Item{ID: p.Post.Uri, Text: fp.Text})
}
return out
}
func apPostsPtrToItems(posts []*activitypub.Post) []clustering.Item {
out := make([]clustering.Item, 0, len(posts))
for _, p := range posts {
if p.Content != "" {
out = append(out, clustering.Item{ID: p.ID, Text: p.Content})
}
}
return out
}
func nostrPostsToItems(posts []*nostr.Post) []clustering.Item {
out := make([]clustering.Item, 0, len(posts))
for _, p := range posts {
if p.Content != "" {
out = append(out, clustering.Item{ID: p.ID, Text: p.Content})
}
}
return out
}
func twitterPostsToItems(posts []*twitter.Post) []clustering.Item {
out := make([]clustering.Item, 0, len(posts))
for _, p := range posts {
if p.Content != "" {
out = append(out, clustering.Item{ID: p.ID, Text: p.Content})
}
}
return out
}
// analytics-specific converters that preserve engagement + timestamps
func blueskyFeedViewsToAnalyticsItems(views []*bsky.FeedDefs_FeedViewPost) []clustering.Item {
out := make([]clustering.Item, 0, len(views))
for _, p := range views {
if p.Post == nil || p.Post.Record == nil {
continue
}
fp, ok := p.Post.Record.Val.(*bsky.FeedPost)
if !ok {
continue
}
item := clustering.Item{ID: p.Post.Uri, Text: fp.Text}
if ts, err := time.Parse(time.RFC3339, p.Post.IndexedAt); err == nil {
item.Timestamp = ts
}
item.Likes = int64Val(p.Post.LikeCount)
item.Reposts = int64Val(p.Post.RepostCount)
item.Replies = int64Val(p.Post.ReplyCount)
item.Quotes = int64Val(p.Post.QuoteCount)
out = append(out, item)
}
return out
}
func apPostsToAnalyticsItems(posts []*activitypub.Post) []clustering.Item {
out := make([]clustering.Item, 0, len(posts))
for _, p := range posts {
if p.Content != "" {
out = append(out, clustering.Item{
ID: p.ID, Text: p.Content, Timestamp: p.CreatedAt,
Likes: int64(p.FavouritesCount), Reposts: int64(p.ReblogsCount), Replies: int64(p.RepliesCount),
})
}
}
return out
}
func nostrPostsToAnalyticsItems(posts []*nostr.Post) []clustering.Item {
out := make([]clustering.Item, 0, len(posts))
for _, p := range posts {
if p.Content != "" {
out = append(out, clustering.Item{ID: p.ID, Text: p.Content, Timestamp: p.CreatedAt})
}
}
return out
}
func twitterPostsToAnalyticsItems(posts []*twitter.Post) []clustering.Item {
out := make([]clustering.Item, 0, len(posts))
for _, p := range posts {
if p.Content == "" {
continue
}
out = append(out, clustering.Item{
ID: p.ID, Text: p.Content, Timestamp: p.CreatedAt,
Likes: int64(p.Likes), Reposts: int64(p.Reposts), Replies: int64(p.Replies), Quotes: int64(p.Quotes),
})
}
return out
}