package utils import ( "encoding/json" "regexp" "strconv" "strings" "time" ) // --------------------------------------------------------- // STRING HELPERS // --------------------------------------------------------- // Truncate returns the first N runes of s, safely. func Truncate(s string, n int) string { rs := []rune(s) if len(rs) <= n { return s } return string(rs[:n]) } // NormalizeUsername lowers and strips unsafe characters. // Used by the Matrix ghost-user generator and Sneed mapping. func NormalizeUsername(s string) string { s = strings.ToLower(strings.TrimSpace(s)) s = strings.ReplaceAll(s, " ", "_") s = strings.ReplaceAll(s, "@", "") s = strings.ReplaceAll(s, ":", "") s = strings.ReplaceAll(s, "#", "") s = strings.ReplaceAll(s, "/", "") s = strings.ReplaceAll(s, "\\", "") return s } // CleanSpaces reduces all whitespace clusters to a single space. func CleanSpaces(s string) string { space := regexp.MustCompile(`\s+`) return space.ReplaceAllString(strings.TrimSpace(s), " ") } // StripControlChars removes non-printable or weird control characters. func StripControlChars(s string) string { re := regexp.MustCompile(`[\x00-\x1F\x7F]`) return re.ReplaceAllString(s, "") } // --------------------------------------------------------- // URL HELPERS // --------------------------------------------------------- // NormalizeURL trims and strips unused trailing punctuation. func NormalizeURL(u string) string { u = strings.TrimSpace(u) if strings.HasSuffix(u, ")") || strings.HasSuffix(u, "]") { u = u[:len(u)-1] } return u } // IsLikelyURL checks for a simple URL pattern. func IsLikelyURL(s string) bool { s = strings.TrimSpace(s) return strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") } // --------------------------------------------------------- // NUMERIC CONVERSIONS // --------------------------------------------------------- // ToInt attempts to convert any JSON-type number to int. func ToInt(v interface{}) (int, bool) { switch t := v.(type) { case int: return t, true case int64: return int(t), true case float64: return int(t), true case string: i, err := strconv.Atoi(t) if err == nil { return i, true } } return 0, false } // --------------------------------------------------------- // JSON SERIALIZATION // --------------------------------------------------------- // MustJSON marshals v or returns a placeholder string. func MustJSON(v interface{}) string { b, err := json.Marshal(v) if err != nil { return "" } return string(b) } // PrettyJSON pretty prints JSON map / slice items. func PrettyJSON(v interface{}) string { b, err := json.MarshalIndent(v, "", " ") if err != nil { return "" } return string(b) } // --------------------------------------------------------- // TIMESTAMP HELPERS // --------------------------------------------------------- // NowMS returns the current Unix time in milliseconds. func NowMS() int64 { return time.Now().UnixNano() / int64(time.Millisecond) } // IsFresher compares two timestamps (ms). // Returns true if tNew is strictly newer than tOld. func IsFresher(tNew, tOld int64) bool { return tNew > tOld } // Age returns the duration since a timestamp in ms. func Age(ts int64) time.Duration { return time.Since(time.UnixMilli(ts)) } // --------------------------------------------------------- // MESSAGE ID HELPERS // --------------------------------------------------------- // IsValidMessageID checks that a Sneedchat message_id is safe. func IsValidMessageID(id int) bool { return id > 0 && id < 1_000_000_000 } // IsValidSyntheticID verifies that bridge synthetic IDs are nonzero. func IsValidSyntheticID(id int) bool { return id > 0 }