mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-25 13:29:27 +01:00
Merge pull request #2074 from slingamn/ircgo_upgrade
upgrade to irc-go v0.4.0
This commit is contained in:
commit
6d642bfe93
2
go.mod
2
go.mod
@ -8,7 +8,7 @@ require (
|
|||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
|
||||||
github.com/ergochat/confusables v0.0.0-20201108231250-4ab98ab61fb1
|
github.com/ergochat/confusables v0.0.0-20201108231250-4ab98ab61fb1
|
||||||
github.com/ergochat/go-ident v0.0.0-20200511222032-830550b1d775
|
github.com/ergochat/go-ident v0.0.0-20200511222032-830550b1d775
|
||||||
github.com/ergochat/irc-go v0.2.0
|
github.com/ergochat/irc-go v0.4.0
|
||||||
github.com/go-sql-driver/mysql v1.7.0
|
github.com/go-sql-driver/mysql v1.7.0
|
||||||
github.com/go-test/deep v1.0.6 // indirect
|
github.com/go-test/deep v1.0.6 // indirect
|
||||||
github.com/gofrs/flock v0.8.1
|
github.com/gofrs/flock v0.8.1
|
||||||
|
4
go.sum
4
go.sum
@ -10,8 +10,8 @@ github.com/ergochat/confusables v0.0.0-20201108231250-4ab98ab61fb1 h1:WLHTOodthV
|
|||||||
github.com/ergochat/confusables v0.0.0-20201108231250-4ab98ab61fb1/go.mod h1:mov+uh1DPWsltdQnOdzn08UO9GsJ3MEvhtu0Ci37fdk=
|
github.com/ergochat/confusables v0.0.0-20201108231250-4ab98ab61fb1/go.mod h1:mov+uh1DPWsltdQnOdzn08UO9GsJ3MEvhtu0Ci37fdk=
|
||||||
github.com/ergochat/go-ident v0.0.0-20200511222032-830550b1d775 h1:QSJIdpr3HOzJDPwxT7hp7WbjoZcS+5GqVvsBscqChk0=
|
github.com/ergochat/go-ident v0.0.0-20200511222032-830550b1d775 h1:QSJIdpr3HOzJDPwxT7hp7WbjoZcS+5GqVvsBscqChk0=
|
||||||
github.com/ergochat/go-ident v0.0.0-20200511222032-830550b1d775/go.mod h1:d2qvgjD0TvGNSvUs+mZgX090RiJlrzUYW6vtANGOy3A=
|
github.com/ergochat/go-ident v0.0.0-20200511222032-830550b1d775/go.mod h1:d2qvgjD0TvGNSvUs+mZgX090RiJlrzUYW6vtANGOy3A=
|
||||||
github.com/ergochat/irc-go v0.2.0 h1:3vHdy4c56UTY6+/rTBrQc1fmt32N5G8PrEZacJDOr+E=
|
github.com/ergochat/irc-go v0.4.0 h1:0YibCKfAAtwxQdNjLQd9xpIEPisLcJ45f8FNsMHAuZc=
|
||||||
github.com/ergochat/irc-go v0.2.0/go.mod h1:2vi7KNpIPWnReB5hmLpl92eMywQvuIeIIGdt/FQCph0=
|
github.com/ergochat/irc-go v0.4.0/go.mod h1:2vi7KNpIPWnReB5hmLpl92eMywQvuIeIIGdt/FQCph0=
|
||||||
github.com/ergochat/scram v1.0.2-ergo1 h1:2bYXiRFQH636pT0msOG39fmEYl4Eq+OuutcyDsCix/g=
|
github.com/ergochat/scram v1.0.2-ergo1 h1:2bYXiRFQH636pT0msOG39fmEYl4Eq+OuutcyDsCix/g=
|
||||||
github.com/ergochat/scram v1.0.2-ergo1/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
github.com/ergochat/scram v1.0.2-ergo1/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||||
github.com/ergochat/websocket v1.4.2-oragono1 h1:plMUunFBM6UoSCIYCKKclTdy/TkkHfUslhOfJQzfueM=
|
github.com/ergochat/websocket v1.4.2-oragono1 h1:plMUunFBM6UoSCIYCKKclTdy/TkkHfUslhOfJQzfueM=
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ergochat/irc-go/ircutils"
|
"github.com/ergochat/irc-go/ircmsg"
|
||||||
|
|
||||||
"github.com/ergochat/ergo/irc/caps"
|
"github.com/ergochat/ergo/irc/caps"
|
||||||
"github.com/ergochat/ergo/irc/datastore"
|
"github.com/ergochat/ergo/irc/datastore"
|
||||||
@ -1191,7 +1191,7 @@ func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffe
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
topic = ircutils.TruncateUTF8Safe(topic, client.server.Config().Limits.TopicLen)
|
topic = ircmsg.TruncateUTF8Safe(topic, client.server.Config().Limits.TopicLen)
|
||||||
|
|
||||||
channel.stateMutex.Lock()
|
channel.stateMutex.Lock()
|
||||||
chname := channel.name
|
chname := channel.name
|
||||||
@ -1450,7 +1450,7 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string, rb
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
comment = ircutils.TruncateUTF8Safe(comment, channel.server.Config().Limits.KickLen)
|
comment = ircmsg.TruncateUTF8Safe(comment, channel.server.Config().Limits.KickLen)
|
||||||
|
|
||||||
message := utils.MakeMessage(comment)
|
message := utils.MakeMessage(comment)
|
||||||
details := client.Details()
|
details := client.Details()
|
||||||
|
@ -451,7 +451,7 @@ func awayHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respons
|
|||||||
var awayMessage string
|
var awayMessage string
|
||||||
if len(msg.Params) > 0 {
|
if len(msg.Params) > 0 {
|
||||||
awayMessage = msg.Params[0]
|
awayMessage = msg.Params[0]
|
||||||
awayMessage = ircutils.TruncateUTF8Safe(awayMessage, server.Config().Limits.AwayLen)
|
awayMessage = ircmsg.TruncateUTF8Safe(awayMessage, server.Config().Limits.AwayLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
wasAway, nowAway := rb.session.SetAway(awayMessage)
|
wasAway, nowAway := rb.session.SetAway(awayMessage)
|
||||||
|
194
vendor/github.com/ergochat/irc-go/ircfmt/ircfmt.go
generated
vendored
194
vendor/github.com/ergochat/irc-go/ircfmt/ircfmt.go
generated
vendored
@ -5,6 +5,7 @@ package ircfmt
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,24 +20,126 @@ const (
|
|||||||
underline string = "\x1f"
|
underline string = "\x1f"
|
||||||
reset string = "\x0f"
|
reset string = "\x0f"
|
||||||
|
|
||||||
runecolour rune = '\x03'
|
metacharacters = (bold + colour + monospace + reverseColour + italic + strikethrough + underline + reset)
|
||||||
runebold rune = '\x02'
|
|
||||||
runemonospace rune = '\x11'
|
|
||||||
runereverseColour rune = '\x16'
|
|
||||||
runeitalic rune = '\x1d'
|
|
||||||
runestrikethrough rune = '\x1e'
|
|
||||||
runereset rune = '\x0f'
|
|
||||||
runeunderline rune = '\x1f'
|
|
||||||
|
|
||||||
// valid characters in a colour code character, for speed
|
|
||||||
colours1 string = "0123456789"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ColorCode is a normalized representation of an IRC color code,
|
||||||
|
// as per this de facto specification: https://modern.ircdocs.horse/formatting.html#color
|
||||||
|
// The zero value of the type represents a default or unset color,
|
||||||
|
// whereas ColorCode{true, 0} represents the color white.
|
||||||
|
type ColorCode struct {
|
||||||
|
IsSet bool
|
||||||
|
Value uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseColor converts a string representation of an IRC color code, e.g. "04",
|
||||||
|
// into a normalized ColorCode, e.g. ColorCode{true, 4}.
|
||||||
|
func ParseColor(str string) (color ColorCode) {
|
||||||
|
// "99 - Default Foreground/Background - Not universally supported."
|
||||||
|
// normalize 99 to ColorCode{} meaning "unset":
|
||||||
|
if code, err := strconv.ParseUint(str, 10, 8); err == nil && code < 99 {
|
||||||
|
color.IsSet = true
|
||||||
|
color.Value = uint8(code)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormattedSubstring represents a section of an IRC message with associated
|
||||||
|
// formatting data.
|
||||||
|
type FormattedSubstring struct {
|
||||||
|
Content string
|
||||||
|
ForegroundColor ColorCode
|
||||||
|
BackgroundColor ColorCode
|
||||||
|
Bold bool
|
||||||
|
Monospace bool
|
||||||
|
Strikethrough bool
|
||||||
|
Underline bool
|
||||||
|
Italic bool
|
||||||
|
ReverseColor bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsFormatted returns whether the section has any formatting flags switched on.
|
||||||
|
func (f *FormattedSubstring) IsFormatted() bool {
|
||||||
|
// could rely on value receiver but if this is to be a public API,
|
||||||
|
// let's make it a pointer receiver
|
||||||
|
g := *f
|
||||||
|
g.Content = ""
|
||||||
|
return g != FormattedSubstring{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// "If there are two ASCII digits available where a <COLOR> is allowed,
|
||||||
|
// then two characters MUST always be read for it and displayed as described below."
|
||||||
|
// we rely on greedy matching to implement this for both forms:
|
||||||
|
// (\x03)00,01
|
||||||
|
colorForeBackRe = regexp.MustCompile(`^([0-9]{1,2}),([0-9]{1,2})`)
|
||||||
|
// (\x03)00
|
||||||
|
colorForeRe = regexp.MustCompile(`^([0-9]{1,2})`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Split takes an IRC message (typically a PRIVMSG or NOTICE final parameter)
|
||||||
|
// containing IRC formatting control codes, and splits it into substrings with
|
||||||
|
// associated formatting information.
|
||||||
|
func Split(raw string) (result []FormattedSubstring) {
|
||||||
|
var chunk FormattedSubstring
|
||||||
|
for {
|
||||||
|
// skip to the next metacharacter, or the end of the string
|
||||||
|
if idx := strings.IndexAny(raw, metacharacters); idx != 0 {
|
||||||
|
if idx == -1 {
|
||||||
|
idx = len(raw)
|
||||||
|
}
|
||||||
|
chunk.Content = raw[:idx]
|
||||||
|
if len(chunk.Content) != 0 {
|
||||||
|
result = append(result, chunk)
|
||||||
|
}
|
||||||
|
raw = raw[idx:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(raw) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're at a metacharacter. by default, all previous formatting carries over
|
||||||
|
metacharacter := raw[0]
|
||||||
|
raw = raw[1:]
|
||||||
|
switch metacharacter {
|
||||||
|
case bold[0]:
|
||||||
|
chunk.Bold = !chunk.Bold
|
||||||
|
case monospace[0]:
|
||||||
|
chunk.Monospace = !chunk.Monospace
|
||||||
|
case strikethrough[0]:
|
||||||
|
chunk.Strikethrough = !chunk.Strikethrough
|
||||||
|
case underline[0]:
|
||||||
|
chunk.Underline = !chunk.Underline
|
||||||
|
case italic[0]:
|
||||||
|
chunk.Italic = !chunk.Italic
|
||||||
|
case reverseColour[0]:
|
||||||
|
chunk.ReverseColor = !chunk.ReverseColor
|
||||||
|
case reset[0]:
|
||||||
|
chunk = FormattedSubstring{}
|
||||||
|
case colour[0]:
|
||||||
|
// preferentially match the "\x0399,01" form, then "\x0399";
|
||||||
|
// if neither of those matches, then it's a reset
|
||||||
|
if matches := colorForeBackRe.FindStringSubmatch(raw); len(matches) != 0 {
|
||||||
|
chunk.ForegroundColor = ParseColor(matches[1])
|
||||||
|
chunk.BackgroundColor = ParseColor(matches[2])
|
||||||
|
raw = raw[len(matches[0]):]
|
||||||
|
} else if matches := colorForeRe.FindStringSubmatch(raw); len(matches) != 0 {
|
||||||
|
chunk.ForegroundColor = ParseColor(matches[1])
|
||||||
|
raw = raw[len(matches[0]):]
|
||||||
|
} else {
|
||||||
|
chunk.ForegroundColor = ColorCode{}
|
||||||
|
chunk.BackgroundColor = ColorCode{}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// should be impossible, but just ignore it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// valtoescape replaces most of IRC characters with our escapes.
|
// valtoescape replaces most of IRC characters with our escapes.
|
||||||
valtoescape = strings.NewReplacer("$", "$$", colour, "$c", reverseColour, "$v", bold, "$b", italic, "$i", strikethrough, "$s", underline, "$u", monospace, "$m", reset, "$r")
|
valtoescape = strings.NewReplacer("$", "$$", colour, "$c", reverseColour, "$v", bold, "$b", italic, "$i", strikethrough, "$s", underline, "$u", monospace, "$m", reset, "$r")
|
||||||
// valToStrip replaces most of the IRC characters with nothing
|
|
||||||
valToStrip = strings.NewReplacer(colour, "$c", reverseColour, "", bold, "", italic, "", strikethrough, "", underline, "", monospace, "", reset, "")
|
|
||||||
|
|
||||||
// escapetoval contains most of our escapes and how they map to real IRC characters.
|
// escapetoval contains most of our escapes and how they map to real IRC characters.
|
||||||
// intentionally skips colour, since that's handled elsewhere.
|
// intentionally skips colour, since that's handled elsewhere.
|
||||||
@ -98,7 +201,9 @@ var (
|
|||||||
"light blue": "12",
|
"light blue": "12",
|
||||||
"pink": "13",
|
"pink": "13",
|
||||||
"grey": "14",
|
"grey": "14",
|
||||||
|
"gray": "14",
|
||||||
"light grey": "15",
|
"light grey": "15",
|
||||||
|
"light gray": "15",
|
||||||
"default": "99",
|
"default": "99",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +228,7 @@ func Escape(in string) string {
|
|||||||
out.WriteString("$c")
|
out.WriteString("$c")
|
||||||
inRunes = inRunes[2:] // strip colour code chars
|
inRunes = inRunes[2:] // strip colour code chars
|
||||||
|
|
||||||
if len(inRunes) < 1 || !strings.Contains(colours1, string(inRunes[0])) {
|
if len(inRunes) < 1 || !isDigit(inRunes[0]) {
|
||||||
out.WriteString("[]")
|
out.WriteString("[]")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -131,14 +236,14 @@ func Escape(in string) string {
|
|||||||
var foreBuffer, backBuffer string
|
var foreBuffer, backBuffer string
|
||||||
foreBuffer += string(inRunes[0])
|
foreBuffer += string(inRunes[0])
|
||||||
inRunes = inRunes[1:]
|
inRunes = inRunes[1:]
|
||||||
if 0 < len(inRunes) && strings.Contains(colours1, string(inRunes[0])) {
|
if 0 < len(inRunes) && isDigit(inRunes[0]) {
|
||||||
foreBuffer += string(inRunes[0])
|
foreBuffer += string(inRunes[0])
|
||||||
inRunes = inRunes[1:]
|
inRunes = inRunes[1:]
|
||||||
}
|
}
|
||||||
if 1 < len(inRunes) && inRunes[0] == ',' && strings.Contains(colours1, string(inRunes[1])) {
|
if 1 < len(inRunes) && inRunes[0] == ',' && isDigit(inRunes[1]) {
|
||||||
backBuffer += string(inRunes[1])
|
backBuffer += string(inRunes[1])
|
||||||
inRunes = inRunes[2:]
|
inRunes = inRunes[2:]
|
||||||
if 0 < len(inRunes) && strings.Contains(colours1, string(inRunes[0])) {
|
if 0 < len(inRunes) && isDigit(inRunes[1]) {
|
||||||
backBuffer += string(inRunes[0])
|
backBuffer += string(inRunes[0])
|
||||||
inRunes = inRunes[1:]
|
inRunes = inRunes[1:]
|
||||||
}
|
}
|
||||||
@ -178,52 +283,27 @@ func Escape(in string) string {
|
|||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isDigit(r rune) bool {
|
||||||
|
return '0' <= r && r <= '9' // don't use unicode.IsDigit, it includes non-ASCII numerals
|
||||||
|
}
|
||||||
|
|
||||||
// Strip takes a raw IRC string and removes it with all formatting codes removed
|
// Strip takes a raw IRC string and removes it with all formatting codes removed
|
||||||
// IE, it turns this: "This is a \x02cool\x02, \x034red\x0f message!"
|
// IE, it turns this: "This is a \x02cool\x02, \x034red\x0f message!"
|
||||||
// into: "This is a cool, red message!"
|
// into: "This is a cool, red message!"
|
||||||
func Strip(in string) string {
|
func Strip(in string) string {
|
||||||
out := strings.Builder{}
|
splitChunks := Split(in)
|
||||||
runes := []rune(in)
|
if len(splitChunks) == 0 {
|
||||||
if out.Len() < len(runes) { // Reduce allocations where needed
|
return ""
|
||||||
out.Grow(len(in) - out.Len())
|
} else if len(splitChunks) == 1 {
|
||||||
}
|
return splitChunks[0].Content
|
||||||
for len(runes) > 0 {
|
|
||||||
switch runes[0] {
|
|
||||||
case runebold, runemonospace, runereverseColour, runeitalic, runestrikethrough, runeunderline, runereset:
|
|
||||||
runes = runes[1:]
|
|
||||||
case runecolour:
|
|
||||||
runes = removeColour(runes)
|
|
||||||
default:
|
|
||||||
out.WriteRune(runes[0])
|
|
||||||
runes = runes[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeNumber(runes []rune) []rune {
|
|
||||||
if len(runes) > 0 && runes[0] >= '0' && runes[0] <= '9' {
|
|
||||||
runes = runes[1:]
|
|
||||||
}
|
|
||||||
return runes
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeColour(runes []rune) []rune {
|
|
||||||
if runes[0] != runecolour {
|
|
||||||
return runes
|
|
||||||
}
|
|
||||||
|
|
||||||
runes = runes[1:]
|
|
||||||
runes = removeNumber(runes)
|
|
||||||
runes = removeNumber(runes)
|
|
||||||
|
|
||||||
if len(runes) > 1 && runes[0] == ',' && runes[1] >= '0' && runes[1] <= '9' {
|
|
||||||
runes = runes[2:]
|
|
||||||
} else {
|
} else {
|
||||||
return runes // Nothing else because we dont have a comma
|
var buf strings.Builder
|
||||||
|
buf.Grow(len(in))
|
||||||
|
for _, chunk := range splitChunks {
|
||||||
|
buf.WriteString(chunk.Content)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
}
|
}
|
||||||
runes = removeNumber(runes)
|
|
||||||
return runes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve "light blue" to "12", "12" to "12", "asdf" to "", etc.
|
// resolve "light blue" to "12", "12" to "12", "asdf" to "", etc.
|
||||||
|
2
vendor/github.com/ergochat/irc-go/ircmsg/message.go
generated
vendored
2
vendor/github.com/ergochat/irc-go/ircmsg/message.go
generated
vendored
@ -238,7 +238,7 @@ func parseLine(line string, maxTagDataLength int, truncateLen int) (ircmsg Messa
|
|||||||
// truncate if desired
|
// truncate if desired
|
||||||
if truncateLen != 0 && truncateLen < len(line) {
|
if truncateLen != 0 && truncateLen < len(line) {
|
||||||
err = ErrorBodyTooLong
|
err = ErrorBodyTooLong
|
||||||
line = line[:truncateLen]
|
line = TruncateUTF8Safe(line, truncateLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// modern: "These message parts, and parameters themselves, are separated
|
// modern: "These message parts, and parameters themselves, are separated
|
||||||
|
29
vendor/github.com/ergochat/irc-go/ircmsg/unicode.go
generated
vendored
Normal file
29
vendor/github.com/ergochat/irc-go/ircmsg/unicode.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright (c) 2021 Shivaram Lingamneni
|
||||||
|
// Released under the MIT License
|
||||||
|
|
||||||
|
package ircmsg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TruncateUTF8Safe truncates a message, respecting UTF8 boundaries. If a message
|
||||||
|
// was originally valid UTF8, TruncateUTF8Safe will not make it invalid; instead
|
||||||
|
// it will truncate additional bytes as needed, back to the last valid
|
||||||
|
// UTF8-encoded codepoint. If a message is not UTF8, TruncateUTF8Safe will truncate
|
||||||
|
// at most 3 additional bytes before giving up.
|
||||||
|
func TruncateUTF8Safe(message string, byteLimit int) (result string) {
|
||||||
|
if len(message) <= byteLimit {
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
message = message[:byteLimit]
|
||||||
|
for i := 0; i < (utf8.UTFMax - 1); i++ {
|
||||||
|
r, n := utf8.DecodeLastRuneInString(message)
|
||||||
|
if r == utf8.RuneError && n <= 1 {
|
||||||
|
message = message[:len(message)-1]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message
|
||||||
|
}
|
19
vendor/github.com/ergochat/irc-go/ircutils/unicode.go
generated
vendored
19
vendor/github.com/ergochat/irc-go/ircutils/unicode.go
generated
vendored
@ -7,24 +7,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/ergochat/irc-go/ircmsg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// truncate a message, taking care not to make valid UTF8 into invalid UTF8
|
var TruncateUTF8Safe = ircmsg.TruncateUTF8Safe
|
||||||
func TruncateUTF8Safe(message string, byteLimit int) (result string) {
|
|
||||||
if len(message) <= byteLimit {
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
message = message[:byteLimit]
|
|
||||||
for i := 0; i < (utf8.UTFMax - 1); i++ {
|
|
||||||
r, n := utf8.DecodeLastRuneInString(message)
|
|
||||||
if r == utf8.RuneError && n <= 1 {
|
|
||||||
message = message[:len(message)-1]
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanitizes human-readable text to make it safe for IRC;
|
// Sanitizes human-readable text to make it safe for IRC;
|
||||||
// assumes UTF-8 and uses the replacement character where
|
// assumes UTF-8 and uses the replacement character where
|
||||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@ -16,7 +16,7 @@ github.com/ergochat/confusables
|
|||||||
# github.com/ergochat/go-ident v0.0.0-20200511222032-830550b1d775
|
# github.com/ergochat/go-ident v0.0.0-20200511222032-830550b1d775
|
||||||
## explicit
|
## explicit
|
||||||
github.com/ergochat/go-ident
|
github.com/ergochat/go-ident
|
||||||
# github.com/ergochat/irc-go v0.2.0
|
# github.com/ergochat/irc-go v0.4.0
|
||||||
## explicit; go 1.15
|
## explicit; go 1.15
|
||||||
github.com/ergochat/irc-go/ircfmt
|
github.com/ergochat/irc-go/ircfmt
|
||||||
github.com/ergochat/irc-go/ircmsg
|
github.com/ergochat/irc-go/ircmsg
|
||||||
|
Loading…
Reference in New Issue
Block a user