183 lines
4.4 KiB
Go

// internal/nostr/poster/poster.go
package poster
import (
"context"
"fmt"
"path/filepath"
"strings"
"time"
"github.com/nbd-wtf/go-nostr"
"git.sovbit.dev/Enki/nostr-poster/internal/media/prepare"
"git.sovbit.dev/Enki/nostr-poster/internal/nostr/events"
"git.sovbit.dev/Enki/nostr-poster/internal/nostr/relay"
"go.uber.org/zap"
)
// Poster handles posting content to Nostr
type Poster struct {
eventMgr *events.EventManager
relayMgr *relay.Manager
mediaPrep *prepare.Manager
logger *zap.Logger
}
// NewPoster creates a new content poster
func NewPoster(
eventMgr *events.EventManager,
relayMgr *relay.Manager,
mediaPrep *prepare.Manager,
logger *zap.Logger,
) *Poster {
if logger == nil {
// Create a default logger if none is provided
var err error
logger, err = zap.NewProduction()
if err != nil {
// If we can't create a logger, use a no-op logger
logger = zap.NewNop()
}
}
return &Poster{
eventMgr: eventMgr,
relayMgr: relayMgr,
mediaPrep: mediaPrep,
logger: logger,
}
}
// PostContent posts content to Nostr
func (p *Poster) PostContent(
pubkey string,
contentPath string,
contentType string,
mediaURL string,
mediaHash string,
caption string,
hashtags []string,
) error {
// Determine the type of content
isImage := strings.HasPrefix(contentType, "image/")
isVideo := strings.HasPrefix(contentType, "video/")
// Create alt text if not provided (initialize it here)
altText := caption
if altText == "" {
// Use the filename without extension as a fallback
altText = strings.TrimSuffix(filepath.Base(contentPath), filepath.Ext(contentPath))
}
// Extract media dimensions if it's an image
if isImage {
dims, err := p.mediaPrep.GetMediaDimensions(contentPath)
if err != nil {
p.logger.Warn("Failed to get image dimensions, continuing anyway",
zap.String("file", contentPath),
zap.Error(err))
} else {
// Log the dimensions
p.logger.Debug("Image dimensions",
zap.Int("width", dims.Width),
zap.Int("height", dims.Height))
// Add dimensions to alt text if available
if altText != "" {
altText = fmt.Sprintf("%s [%dx%d]", altText, dims.Width, dims.Height)
}
}
}
// Create a context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
var event *nostr.Event
var err error
// Determine the appropriate event kind and create the event
if isImage {
// Use kind 1 (text note) for images
// Create hashtag string for post content
var hashtagStr string
if len(hashtags) > 0 {
hashtagArr := make([]string, len(hashtags))
for i, tag := range hashtags {
hashtagArr[i] = "#" + tag
}
hashtagStr = "\n\n" + strings.Join(hashtagArr, " ")
}
content := caption + hashtagStr
event, err = p.eventMgr.CreateAndSignMediaEvent(
pubkey,
content,
mediaURL,
contentType,
mediaHash,
altText,
hashtags,
)
} else if isVideo {
// For videos, determine if it's a short video
isShortVideo := false // Just a placeholder, would need logic to determine
// Create the video event
event, err = p.eventMgr.CreateAndSignVideoEvent(
pubkey,
caption, // Title
caption, // Description
mediaURL,
contentType,
mediaHash,
"", // Preview image URL
0, // Duration
altText,
hashtags,
isShortVideo,
)
} else {
// For other types, use a regular text note with attachment
event, err = p.eventMgr.CreateAndSignMediaEvent(
pubkey,
caption,
mediaURL,
contentType,
mediaHash,
altText,
hashtags,
)
}
if err != nil {
return fmt.Errorf("failed to create event: %w", err)
}
// Publish the event
relays, err := p.relayMgr.PublishEvent(ctx, event)
if err != nil {
return fmt.Errorf("failed to publish event: %w", err)
}
p.logger.Info("Published content to relays",
zap.String("event_id", event.ID),
zap.Strings("relays", relays))
return nil
}
// CreatePostContentFunc creates a function for posting content
func CreatePostContentFunc(
eventMgr *events.EventManager,
relayMgr *relay.Manager,
mediaPrep *prepare.Manager,
logger *zap.Logger,
) func(string, string, string, string, string, string, []string) error {
poster := NewPoster(eventMgr, relayMgr, mediaPrep, logger)
return func(pubkey, contentPath, contentType, mediaURL, mediaHash, caption string, hashtags []string) error {
return poster.PostContent(pubkey, contentPath, contentType, mediaURL, mediaHash, caption, hashtags)
}
}