package utils import ( "regexp" "strings" ) // BBCodeToMarkdown converts simplified Siropu-style BBCode into Matrix-safe Markdown. // // RULES: // - [img]URL[/img] remains an image but without video tagging // - [url] and [url=...] become markdown links // - All [video]...[/video] wrappers are REMOVED entirely (your requirement) // - Bold/italic/underline basic BBCode converted to markdown // - Unknown tags stripped and inner text preserved // func BBCodeToMarkdown(in string) string { if in == "" { return "" } s := in // ---------------------------------------------------- // STRIP VIDEO TAGS COMPLETELY // ---------------------------------------------------- s = stripTagCompletely(s, "video") // ---------------------------------------------------- // IMAGE TAGS → leave as-is, but sanitize formatting // ---------------------------------------------------- s = regexp.MustCompile(`(?i)\[img\](.*?)\[/img\]`).ReplaceAllString(s, "![]($1)") // ---------------------------------------------------- // URL TAGS → Markdown links // ---------------------------------------------------- // [url]http://x[/url] s = regexp.MustCompile(`(?i)\[url\](.*?)\[/url\]`).ReplaceAllString(s, "[$1]($1)") // [url=http://x]label[/url] s = regexp.MustCompile(`(?i)\[url=(.*?)\](.*?)\[/url\]`).ReplaceAllString(s, "[$2]($1)") // ---------------------------------------------------- // BASIC FORMATTING → Markdown // ---------------------------------------------------- replacements := map[*regexp.Regexp]string{ regexp.MustCompile(`(?i)\[b\](.*?)\[/b\]`): "**$1**", regexp.MustCompile(`(?i)\[i\](.*?)\[/i\]`): "*$1*", regexp.MustCompile(`(?i)\[u\](.*?)\[/u\]`): "__$1__", regexp.MustCompile(`(?i)\[s\](.*?)\[/s\]`): "~~$1~~", regexp.MustCompile(`(?i)\[quote\](.*?)\[/quote\]`): "> $1", } for re, repl := range replacements { s = re.ReplaceAllString(s, repl) } // ---------------------------------------------------- // REMOVE ANY OTHER BBCODE TAGS, KEEP CONTENT // ---------------------------------------------------- s = regexp.MustCompile(`(?i)\[(\/?)[a-zA-Z0-9\=\#]+?\]`).ReplaceAllString(s, "") // Cleanup whitespace return strings.TrimSpace(s) } // stripTagCompletely removes [tag]...[/tag] entirely, preserving inner text only if desired. // Here we drop everything inside video tags. func stripTagCompletely(s, tag string) string { re := regexp.MustCompile(`(?is)\[` + tag + `(?:=[^\]]*)?\].*?\[\/` + tag + `\]`) return re.ReplaceAllString(s, "") } // IsImageURL determines whether a string looks like an image link. // Used by both Matrix → Sneed and Sneed → Matrix paths. func IsImageURL(u string) bool { u = strings.ToLower(strings.TrimSpace(u)) if !(strings.HasPrefix(u, "http://") || strings.HasPrefix(u, "https://")) { return false } // strip query if i := strings.Index(u, "?"); i > 0 { u = u[:i] } return strings.HasSuffix(u, ".png") || strings.HasSuffix(u, ".jpg") || strings.HasSuffix(u, ".jpeg") || strings.HasSuffix(u, ".gif") || strings.HasSuffix(u, ".webp") } // WrapImageForSneed produces the BBCode wrapper used for outbound // Matrix → Sneed image messages. // // Example: // input: "https://example.com/img.jpg" // output: "[url=https://example.com/img.jpg][img]https://example.com/img.jpg[/img][/url]" // func WrapImageForSneed(url string) string { if url == "" { return "" } return "[url=" + url + "][img]" + url + "[/img][/url]" } // ExtractFirstURL finds the first URL-like token in a message. // Useful for deciding if a message is an image-only post. func ExtractFirstURL(s string) string { re := regexp.MustCompile(`https?://[^\s]+`) found := re.FindString(s) return found }