package threadspost import ( "fmt" "strings" ) // Threads Graph API text post hard limit (main post body). const MaxPublishRunes = 500 // Threads reply/comment limit (outreach, not main post). const MaxReplyRunes = 1000 // ViralSweetMin/Max: engagement sweet spot on Threads feed (Buffer / creator data, 2024–2026). const ViralSweetMin = 80 const ViralSweetMax = 220 // ViralSoftWarn: engagement tends to drop beyond this length in the main visible post. const ViralSoftWarn = 300 type LengthBand string const ( BandEmpty LengthBand = "empty" BandTooShort LengthBand = "too_short" BandSweet LengthBand = "sweet" BandLong LengthBand = "long" BandOverSoft LengthBand = "over_soft" BandOverHard LengthBand = "over_hard" ) func RuneLen(text string) int { return len([]rune(strings.TrimSpace(text))) } func ClampPublish(text string) string { text = strings.TrimSpace(text) runes := []rune(text) if len(runes) > MaxPublishRunes { return string(runes[:MaxPublishRunes]) } return text } func PublishBand(text string) LengthBand { n := RuneLen(text) switch { case n == 0: return BandEmpty case n < ViralSweetMin: return BandTooShort case n <= ViralSweetMax: return BandSweet case n <= ViralSoftWarn: return BandLong case n <= MaxPublishRunes: return BandOverSoft default: return BandOverHard } } func ValidatePublish(text string) error { if strings.TrimSpace(text) == "" { return fmt.Errorf("貼文內文不可為空") } n := RuneLen(text) if n > MaxPublishRunes { return fmt.Errorf("貼文超過 Threads 上限 %d 字(目前 %d 字)", MaxPublishRunes, n) } return nil } func ValidateReply(text string) error { if strings.TrimSpace(text) == "" { return fmt.Errorf("留言內文不可為空") } n := RuneLen(text) if n > MaxReplyRunes { return fmt.Errorf("留言超過 Threads 上限 %d 字(目前 %d 字)", MaxReplyRunes, n) } return nil } func PublishHint(text string) string { switch PublishBand(text) { case BandTooShort: return fmt.Sprintf("偏短(爆款常見 %d~%d 字)", ViralSweetMin, ViralSweetMax) case BandSweet: return fmt.Sprintf("長度適中(爆款常見 %d~%d 字)", ViralSweetMin, ViralSweetMax) case BandLong: return fmt.Sprintf("略長(超過 %d 字互動可能下降)", ViralSoftWarn) case BandOverSoft: return fmt.Sprintf("接近 Threads 主文上限 %d 字", MaxPublishRunes) case BandOverHard: return fmt.Sprintf("超過 Threads 上限 %d 字,請縮短", MaxPublishRunes) default: return "" } }