101 lines
2.5 KiB
Go
101 lines
2.5 KiB
Go
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 ""
|
||
}
|
||
}
|