Modularized
This commit is contained in:
61
utils/bbcode.go
Normal file
61
utils/bbcode.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func BBCodeToMarkdown(text string) string {
|
||||
if text == "" {
|
||||
return ""
|
||||
}
|
||||
text = strings.ReplaceAll(text, "\r\n", "\n")
|
||||
text = strings.ReplaceAll(text, "\r", "\n")
|
||||
|
||||
text = regexp.MustCompile(`(?i)\[img\](.*?)\[/img\]`).ReplaceAllString(text, "$1")
|
||||
text = regexp.MustCompile(`(?i)\[video\](.*?)\[/video\]`).ReplaceAllString(text, "$1")
|
||||
|
||||
urlPattern := regexp.MustCompile(`(?i)\[url=(.*?)\](.*?)\[/url\]`)
|
||||
text = urlPattern.ReplaceAllStringFunc(text, func(match string) string {
|
||||
parts := urlPattern.FindStringSubmatch(match)
|
||||
if len(parts) < 3 {
|
||||
return match
|
||||
}
|
||||
link := strings.TrimSpace(parts[1])
|
||||
txt := strings.TrimSpace(parts[2])
|
||||
if regexp.MustCompile(`(?i)^https?://`).MatchString(txt) {
|
||||
return txt
|
||||
}
|
||||
return "[" + txt + "](" + link + ")"
|
||||
})
|
||||
|
||||
text = regexp.MustCompile(`(?i)\[url\](.*?)\[/url\]`).ReplaceAllString(text, "$1")
|
||||
text = regexp.MustCompile(`(?i)\[(?:b|strong)\](.*?)\[/\s*(?:b|strong)\]`).ReplaceAllString(text, "**$1**")
|
||||
text = regexp.MustCompile(`(?i)\[(?:i|em)\](.*?)\[/\s*(?:i|em)\]`).ReplaceAllString(text, "*$1*")
|
||||
text = regexp.MustCompile(`(?i)\[u\](.*?)\[/\s*u\]`).ReplaceAllString(text, "__$1__")
|
||||
text = regexp.MustCompile(`(?i)\[(?:s|strike)\](.*?)\[/\s*(?:s|strike)\]`).ReplaceAllString(text, "~~$1~~")
|
||||
text = regexp.MustCompile(`(?i)\[code\](.*?)\[/code\]`).ReplaceAllString(text, "`$1`")
|
||||
text = regexp.MustCompile(`(?i)\[(?:php|plain|code=\w+)\](.*?)\[/(?:php|plain|code)\]`).ReplaceAllString(text, "```$1```")
|
||||
|
||||
quotePattern := regexp.MustCompile(`(?i)\[quote\](.*?)\[/quote\]`)
|
||||
text = quotePattern.ReplaceAllStringFunc(text, func(match string) string {
|
||||
parts := quotePattern.FindStringSubmatch(match)
|
||||
if len(parts) < 2 {
|
||||
return match
|
||||
}
|
||||
inner := strings.TrimSpace(parts[1])
|
||||
lines := strings.Split(inner, "\n")
|
||||
for i, line := range lines {
|
||||
lines[i] = "> " + line
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
})
|
||||
|
||||
text = regexp.MustCompile(`(?i)\[spoiler\](.*?)\[/spoiler\]`).ReplaceAllString(text, "||$1||")
|
||||
text = regexp.MustCompile(`(?i)\[(?:color|size)=.*?\](.*?)\[/\s*(?:color|size)\]`).ReplaceAllString(text, "$1")
|
||||
text = regexp.MustCompile(`(?m)^\[\*\]\s*`).ReplaceAllString(text, "• ")
|
||||
text = regexp.MustCompile(`(?i)\[/?list\]`).ReplaceAllString(text, "")
|
||||
text = regexp.MustCompile(`\[/?[A-Za-z0-9\-=_]+\]`).ReplaceAllString(text, "")
|
||||
|
||||
return strings.TrimSpace(text)
|
||||
}
|
||||
98
utils/boundedmap.go
Normal file
98
utils/boundedmap.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BoundedMap struct {
|
||||
mu sync.RWMutex
|
||||
data map[int]interface{}
|
||||
timestamps map[int]time.Time
|
||||
maxSize int
|
||||
maxAge time.Duration
|
||||
keys []int
|
||||
}
|
||||
|
||||
func NewBoundedMap(maxSize int, maxAge time.Duration) *BoundedMap {
|
||||
return &BoundedMap{
|
||||
data: make(map[int]interface{}),
|
||||
timestamps: make(map[int]time.Time),
|
||||
maxSize: maxSize,
|
||||
maxAge: maxAge,
|
||||
keys: make([]int, 0, maxSize),
|
||||
}
|
||||
}
|
||||
|
||||
func (bm *BoundedMap) Set(key int, value interface{}) {
|
||||
bm.mu.Lock()
|
||||
defer bm.mu.Unlock()
|
||||
if _, ok := bm.data[key]; ok {
|
||||
bm.data[key] = value
|
||||
bm.timestamps[key] = time.Now()
|
||||
for i, k := range bm.keys {
|
||||
if k == key {
|
||||
bm.keys = append(bm.keys[:i], bm.keys[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
bm.keys = append(bm.keys, key)
|
||||
return
|
||||
}
|
||||
bm.data[key] = value
|
||||
bm.timestamps[key] = time.Now()
|
||||
bm.keys = append(bm.keys, key)
|
||||
if len(bm.data) > bm.maxSize {
|
||||
oldest := bm.keys[0]
|
||||
delete(bm.data, oldest)
|
||||
delete(bm.timestamps, oldest)
|
||||
bm.keys = bm.keys[1:]
|
||||
}
|
||||
}
|
||||
|
||||
func (bm *BoundedMap) Get(key int) (interface{}, bool) {
|
||||
bm.mu.RLock()
|
||||
defer bm.mu.RUnlock()
|
||||
v, ok := bm.data[key]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func (bm *BoundedMap) Delete(key int) {
|
||||
bm.mu.Lock()
|
||||
defer bm.mu.Unlock()
|
||||
delete(bm.data, key)
|
||||
delete(bm.timestamps, key)
|
||||
for i, k := range bm.keys {
|
||||
if k == key {
|
||||
bm.keys = append(bm.keys[:i], bm.keys[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (bm *BoundedMap) CleanupOldEntries() int {
|
||||
bm.mu.Lock()
|
||||
defer bm.mu.Unlock()
|
||||
now := time.Now()
|
||||
removed := 0
|
||||
for key, ts := range bm.timestamps {
|
||||
if now.Sub(ts) > bm.maxAge {
|
||||
delete(bm.data, key)
|
||||
delete(bm.timestamps, key)
|
||||
for i, k := range bm.keys {
|
||||
if k == key {
|
||||
bm.keys = append(bm.keys[:i], bm.keys[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
removed++
|
||||
}
|
||||
}
|
||||
return removed
|
||||
}
|
||||
|
||||
func (bm *BoundedMap) Len() int {
|
||||
bm.mu.RLock()
|
||||
defer bm.mu.RUnlock()
|
||||
return len(bm.data)
|
||||
}
|
||||
8
utils/helpers.go
Normal file
8
utils/helpers.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package utils
|
||||
|
||||
func Min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
Reference in New Issue
Block a user