Files
Sneedchat-Matrix-Bridge-Go/utils/helpers.go
2025-11-18 02:07:08 -05:00

144 lines
3.8 KiB
Go

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 "<json error>"
}
return string(b)
}
// PrettyJSON pretty prints JSON map / slice items.
func PrettyJSON(v interface{}) string {
b, err := json.MarshalIndent(v, "", " ")
if err != nil {
return "<json error>"
}
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
}