-
Notifications
You must be signed in to change notification settings - Fork 16
/
utils.go
100 lines (88 loc) · 2.98 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package utils
import (
"net/netip"
"regexp"
"strings"
"github.com/JohannesKaufmann/html-to-markdown/escape"
)
// GetUniquifiedIP returns a uniquified version of an IP address
// (sets the lower bits of a IPv6 to zero, leaves IPv4 untouched)
func GetUniquifiedIP(remoteAddress string) string {
addr, err := netip.ParseAddr(remoteAddress)
if err != nil {
return remoteAddress
}
keepTopBits := 64
if keepTopBits > addr.BitLen() {
keepTopBits = addr.BitLen()
}
prefix, err := addr.Prefix(keepTopBits)
if err != nil {
return remoteAddress
}
return prefix.Addr().Unmap().WithZone("").String()
}
// ReplaceAllStringSubmatchFunc is a version of func (*regexp.Regexp) ReplaceAllStringFunc
// that passes submatches to the callback.
// It follows the "semantic naming" convention for functions in the regexp package.
// Based on the implementation found at
// https://elliotchance.medium.com/go-replace-string-with-regular-expression-callback-f89948bad0bb
func ReplaceAllStringSubmatchFunc(re *regexp.Regexp, str string, repl func([]string) string) string {
result := ""
lastIndex := 0
for _, v := range re.FindAllSubmatchIndex([]byte(str), -1) {
groups := []string{}
for i := 0; i < len(v); i += 2 {
group := ""
if v[i] >= 0 {
group = str[v[i]:v[i+1]]
}
groups = append(groups, group)
}
result += str[lastIndex:v[0]] + repl(groups)
lastIndex = v[1]
}
return result + str[lastIndex:]
}
// ReplaceAllStringSubmatchFuncExcludingInside works like ReplaceAllStringSubmatchFunc,
// but it does not perform any replacements in regions that match the regexp passed as the second argument
func ReplaceAllStringSubmatchFuncExcludingInside(re, excludeInside *regexp.Regexp, str string, repl func([]string) string) string {
result := ""
lastIndex := 0
for _, v := range excludeInside.FindAllIndex([]byte(str), -1) {
// pass all from the last match up to here excluding what's in the excludeInside match
result += ReplaceAllStringSubmatchFunc(re, str[lastIndex:v[0]], repl)
// what's inside the match goes as-is
result += str[v[0]:v[1]]
lastIndex = v[1]
}
// append what's outside of any match. we need to pass that through as well
result += ReplaceAllStringSubmatchFunc(re, str[lastIndex:], repl)
return result
}
// CastStringLikeSlice converts between slices of string-like types
func CastStringLikeSlice[T ~string, V ~string](in []T) []V {
result := make([]V, len(in))
for i, t := range in {
result[i] = V(t)
}
return result
}
// SliceToSet returns a map-based set with the elements of the specified slice
func SliceToSet[T comparable](s []T) map[T]struct{} {
set := make(map[T]struct{})
for _, item := range s {
set[item] = struct{}{}
}
return set
}
var replacer = strings.NewReplacer(
`~`, `\~`,
)
// EscapeMarkdown is like escape.MarkdownCharacters but aware of JungleTV-specific chat markdown extensions
func EscapeMarkdownCharacters(s string) string {
s = escape.MarkdownCharacters(s)
s = replacer.Replace(s)
// TODO do something about emotes and timestamps
return s
}