144 lines
3.8 KiB
Go
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
|
|
}
|