Compare commits
5 Commits
master
...
split-mess
Author | SHA1 | Date | |
---|---|---|---|
|
662a911d11 | ||
|
e2b3e11741 | ||
|
14813a795d | ||
|
b472e83947 | ||
|
6f29ca92da |
24
.github/workflows/test.yml
vendored
Normal file
24
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
name: test
|
||||
|
||||
on:
|
||||
push: {}
|
||||
pull_request: { branches: [master] }
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with: { go-version: '1.x' }
|
||||
- uses: actions/checkout@v2
|
||||
- name: setup
|
||||
run: |
|
||||
go get -v golang.org/x/lint/golint
|
||||
- name: lint
|
||||
run: golint -min_confidence 0.9 -set_exit_status
|
||||
- name: test
|
||||
run: |
|
||||
GORACE="exitcode=1 halt_on_error=1" go test -v -coverprofile=coverage.txt -race -timeout 3m -count 3 -cpu 1,4
|
||||
bash <(curl -s https://codecov.io/bash)
|
||||
- name: vet
|
||||
run: go vet -v .
|
25
.travis.yml
25
.travis.yml
@ -1,25 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.11.x
|
||||
- tip
|
||||
before_install:
|
||||
- go get -v golang.org/x/lint/golint
|
||||
script:
|
||||
- $HOME/gopath/bin/golint -min_confidence 0.9 -set_exit_status
|
||||
- GORACE="exitcode=1 halt_on_error=1" go test -v -coverprofile=coverage.txt -race -timeout 3m -count 3 -cpu 1,4
|
||||
- go vet -v .
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- irc.byteirc.org#/dev/null
|
||||
template:
|
||||
- "%{repository} #%{build_number} %{branch}/%{commit}: %{author} -- %{message}
|
||||
%{build_url}"
|
||||
on_success: change
|
||||
on_failure: change
|
||||
skip_join: false
|
@ -1,11 +1,11 @@
|
||||
<p align="center"><a href="https://godoc.org/github.com/lrstanley/girc"><img width="270" src="http://i.imgur.com/DEnyrdB.png"></a></p>
|
||||
<p align="center">girc, a flexible IRC library for Go</p>
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/lrstanley/girc"><img src="https://travis-ci.org/lrstanley/girc.svg?branch=master" alt="Build Status"></a>
|
||||
<a href="https://github.com/lrstanley/girc/actions"><img src="https://github.com/lrstanley/girc/workflows/test/badge.svg" alt="Test Status"></a>
|
||||
<a href="https://codecov.io/gh/lrstanley/girc"><img src="https://codecov.io/gh/lrstanley/girc/branch/master/graph/badge.svg" alt="Coverage Status"></a>
|
||||
<a href="https://godoc.org/github.com/lrstanley/girc"><img src="https://godoc.org/github.com/lrstanley/girc?status.png" alt="GoDoc"></a>
|
||||
<a href="https://pkg.go.dev/github.com/lrstanley/girc"><img src="https://pkg.go.dev/badge/github.com/lrstanley/girc" alt="GoDoc"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/lrstanley/girc"><img src="https://goreportcard.com/badge/github.com/lrstanley/girc" alt="Go Report Card"></a>
|
||||
<a href="https://byteirc.org/channel/%23%2Fdev%2Fnull"><img src="https://img.shields.io/badge/ByteIRC-%23%2Fdev%2Fnull-blue.svg" alt="IRC Chat"></a>
|
||||
<a href="https://liam.sh/chat"><img src="https://img.shields.io/badge/community-chat%20with%20us-green.svg" alt="Community Chat"></a>
|
||||
</p>
|
||||
|
||||
## Status
|
||||
|
42
builtin.go
42
builtin.go
@ -405,6 +405,48 @@ func handleISUPPORT(c *Client, e Event) {
|
||||
c.state.serverOptions[name] = val
|
||||
}
|
||||
c.state.Unlock()
|
||||
|
||||
// Check for max line/nick/user/host lengths here.
|
||||
c.state.RLock()
|
||||
maxLineLength := c.state.maxLineLength
|
||||
c.state.RUnlock()
|
||||
maxNickLength := defaultNickLength
|
||||
maxUserLength := defaultUserLength
|
||||
maxHostLength := defaultHostLength
|
||||
|
||||
var ok bool
|
||||
var tmp int
|
||||
|
||||
if tmp, ok = c.GetServerOptionInt("LINELEN"); ok {
|
||||
maxLineLength = tmp
|
||||
c.state.Lock()
|
||||
c.state.maxLineLength = maxTagLength - 2 // -2 for CR-LF.
|
||||
c.state.Unlock()
|
||||
}
|
||||
|
||||
if tmp, ok = c.GetServerOptionInt("NICKLEN"); ok {
|
||||
maxNickLength = tmp
|
||||
}
|
||||
if tmp, ok = c.GetServerOptionInt("MAXNICKLEN"); ok && tmp > maxNickLength {
|
||||
maxNickLength = tmp
|
||||
}
|
||||
if tmp, ok = c.GetServerOptionInt("USERLEN"); ok && tmp > maxUserLength {
|
||||
maxUserLength = tmp
|
||||
}
|
||||
if tmp, ok = c.GetServerOptionInt("HOSTLEN"); ok && tmp > maxHostLength {
|
||||
maxHostLength = tmp
|
||||
}
|
||||
|
||||
prefixLen := defaultPrefixPadding + maxNickLength + maxUserLength + maxHostLength
|
||||
if prefixLen >= maxLineLength {
|
||||
// Give up and go with defaults.
|
||||
c.state.notify(c, UPDATE_GENERAL)
|
||||
return
|
||||
}
|
||||
c.state.Lock()
|
||||
c.state.maxPrefixLength = prefixLen
|
||||
c.state.Unlock()
|
||||
|
||||
c.state.notify(c, UPDATE_GENERAL)
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ func TestTagGetSetCount(t *testing.T) {
|
||||
}
|
||||
|
||||
// Add a hidden ascii value at the end to make it invalid.
|
||||
if err := e.Tags.Set("key", "invalid-value"+string(0x08)); err == nil {
|
||||
if err := e.Tags.Set("key", "invalid-value"+string(rune(0x08))); err == nil {
|
||||
t.Fatal("tag set of invalid value should have returned error")
|
||||
}
|
||||
}
|
||||
|
38
client.go
38
client.go
@ -678,6 +678,29 @@ func (c *Client) GetServerOption(key string) (result string, ok bool) {
|
||||
return result, ok
|
||||
}
|
||||
|
||||
// GetServerOptionInt retrieves a server capability setting (as an integer) that was
|
||||
// retrieved during client connection. This is also known as ISUPPORT (or RPL_PROTOCTL).
|
||||
// Will panic if used when tracking has been disabled. Examples of usage:
|
||||
//
|
||||
// nickLen, success := GetServerOption("MAXNICKLEN")
|
||||
//
|
||||
func (c *Client) GetServerOptionInt(key string) (result int, ok bool) {
|
||||
var data string
|
||||
var err error
|
||||
|
||||
data, ok = c.GetServerOption(key)
|
||||
if !ok {
|
||||
return result, ok
|
||||
}
|
||||
|
||||
result, err = strconv.Atoi(data)
|
||||
if err != nil {
|
||||
ok = false
|
||||
}
|
||||
|
||||
return result, ok
|
||||
}
|
||||
|
||||
// NetworkName returns the network identifier. E.g. "EsperNet", "ByteIRC".
|
||||
// May be empty if the server does not support RPL_ISUPPORT (or RPL_PROTOCTL).
|
||||
// Will panic if used when tracking has been disabled.
|
||||
@ -752,6 +775,21 @@ func (c *Client) HasCapability(name string) (has bool) {
|
||||
return has
|
||||
}
|
||||
|
||||
// MaxEventLength return the maximum supported server length of an event. This is the
|
||||
// maximum length of the command and arguments, excluding the source/prefix supported
|
||||
// by the protocol. If state tracking is enabled, this will utilize ISUPPORT/IRCv3
|
||||
// information to more accurately calculate the maximum supported length (i.e. extended
|
||||
// length events).
|
||||
func (c *Client) MaxEventLength() (max int) {
|
||||
if !c.Config.disableTracking {
|
||||
c.state.RLock()
|
||||
max = c.state.maxLineLength - c.state.maxPrefixLength
|
||||
c.state.RUnlock()
|
||||
return max
|
||||
}
|
||||
return DefaultMaxLineLength - DefaultMaxPrefixLength
|
||||
}
|
||||
|
||||
// panicIfNotTracking will throw a panic when it's called, and tracking is
|
||||
// disabled. Adds useful info like what function specifically, and where it
|
||||
// was called from.
|
||||
|
@ -25,7 +25,7 @@ func (cmd *Commands) Nick(name string) {
|
||||
func (cmd *Commands) Join(channels ...string) {
|
||||
// We can join multiple channels at once, however we need to ensure that
|
||||
// we are not exceeding the line length. (see maxLength)
|
||||
max := maxLength - len(JOIN) - 1
|
||||
max := cmd.c.MaxEventLength() - len(JOIN) - 1
|
||||
|
||||
var buffer string
|
||||
|
||||
@ -329,7 +329,7 @@ func (cmd *Commands) List(channels ...string) {
|
||||
|
||||
// We can LIST multiple channels at once, however we need to ensure that
|
||||
// we are not exceeding the line length. (see maxLength)
|
||||
max := maxLength - len(JOIN) - 1
|
||||
max := cmd.c.MaxEventLength() - len(JOIN) - 1
|
||||
|
||||
var buffer string
|
||||
|
||||
@ -356,7 +356,7 @@ func (cmd *Commands) List(channels ...string) {
|
||||
// Whowas sends a WHOWAS query to the server. amount is the amount of results
|
||||
// you want back.
|
||||
func (cmd *Commands) Whowas(user string, amount int) {
|
||||
cmd.c.Send(&Event{Command: WHOWAS, Params: []string{user, string(amount)}})
|
||||
cmd.c.Send(&Event{Command: WHOWAS, Params: []string{user, fmt.Sprint(amount)}})
|
||||
}
|
||||
|
||||
// Monitor sends a MONITOR query to the server. The results of the query
|
||||
|
48
conn.go
48
conn.go
@ -427,35 +427,41 @@ func (c *Client) readLoop(ctx context.Context, errs chan error, wg *sync.WaitGro
|
||||
}
|
||||
}
|
||||
|
||||
// Send sends an event to the server. Use Client.RunHandlers() if you are
|
||||
// simply looking to trigger handlers with an event.
|
||||
// Send sends an event to the server. Send will split events if the event is longer than
|
||||
// what the server supports, and is an event that supports splitting. Use
|
||||
// Client.RunHandlers() if you are simply looking to trigger handlers with an event.
|
||||
func (c *Client) Send(event *Event) {
|
||||
var delay time.Duration
|
||||
|
||||
if !c.Config.AllowFlood {
|
||||
c.mu.RLock()
|
||||
|
||||
// Drop the event early as we're disconnected, this way we don't have to wait
|
||||
// the (potentially long) rate limit delay before dropping.
|
||||
if c.conn == nil {
|
||||
c.debugLogEvent(event, true)
|
||||
c.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
c.conn.mu.Lock()
|
||||
delay = c.conn.rate(event.Len())
|
||||
c.conn.mu.Unlock()
|
||||
c.mu.RUnlock()
|
||||
}
|
||||
|
||||
if c.Config.GlobalFormat && len(event.Params) > 0 && event.Params[len(event.Params)-1] != "" &&
|
||||
(event.Command == PRIVMSG || event.Command == TOPIC || event.Command == NOTICE) {
|
||||
event.Params[len(event.Params)-1] = Fmt(event.Params[len(event.Params)-1])
|
||||
}
|
||||
|
||||
<-time.After(delay)
|
||||
c.write(event)
|
||||
var events []*Event
|
||||
events = event.split(c.MaxEventLength())
|
||||
|
||||
for _, e := range events {
|
||||
if !c.Config.AllowFlood {
|
||||
c.mu.RLock()
|
||||
|
||||
// Drop the event early as we're disconnected, this way we don't have to wait
|
||||
// the (potentially long) rate limit delay before dropping.
|
||||
if c.conn == nil {
|
||||
c.debugLogEvent(e, true)
|
||||
c.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
c.conn.mu.Lock()
|
||||
delay = c.conn.rate(e.Len())
|
||||
c.conn.mu.Unlock()
|
||||
c.mu.RUnlock()
|
||||
}
|
||||
|
||||
<-time.After(delay)
|
||||
c.write(e)
|
||||
}
|
||||
}
|
||||
|
||||
// write is the lower level function to write an event. It does not have a
|
||||
|
130
event.go
130
event.go
@ -13,7 +13,41 @@ import (
|
||||
|
||||
const (
|
||||
eventSpace byte = ' ' // Separator.
|
||||
maxLength = 510 // Maximum length is 510 (2 for line endings).
|
||||
|
||||
// TODO: if state tracking is enabled, we SHOULD be able to use it's known length.
|
||||
|
||||
// Can be overridden by the NICKLEN (or MAXNICKLEN) ISUPPORT parameter. 30 or 31
|
||||
// are typical values for this parameter advertised by servers today.
|
||||
defaultNickLength = 30
|
||||
// The maximum length of <username> may be specified by the USERLEN RPL_ISUPPORT
|
||||
// parameter. If this length is advertised, the username MUST be silently truncated
|
||||
// to the given length before being used.
|
||||
defaultUserLength = 18
|
||||
// If a looked-up domain name is longer than this length (or overridden by the
|
||||
// HOSTLEN ISUPPORT parameter), the server SHOULD opt to use the IP address instead,
|
||||
// so that the hostname is underneath this length.
|
||||
defaultHostLength = 63
|
||||
|
||||
// defaultPrefixPadding defaults the estimated prefix padding length of a given
|
||||
// event. See also:
|
||||
// [ ":" ( servername / ( nickname [ [ "!" user ] "@" host ] ) ) SPACE ]
|
||||
defaultPrefixPadding = 4
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultMaxLineLength is the default maximum length for an event. 510 (+2 for line endings)
|
||||
// is used as a default as this is used by many older implementations.
|
||||
//
|
||||
// See also: RFC 2812
|
||||
// IRC messages are always lines of characters terminated with a CR-LF
|
||||
// (Carriage Return - Line Feed) pair, and these messages SHALL NOT
|
||||
// exceed 512 characters in length, counting all characters including
|
||||
// the trailing CR-LF.
|
||||
DefaultMaxLineLength = 510
|
||||
|
||||
// DefaultMaxPrefixLength defines the default max ":nickname!user@host " length
|
||||
// that's used to calculate line splitting.
|
||||
DefaultMaxPrefixLength = defaultPrefixPadding + defaultNickLength + defaultUserLength + defaultHostLength
|
||||
)
|
||||
|
||||
// cutCRFunc is used to trim CR characters from prefixes/messages.
|
||||
@ -223,11 +257,82 @@ func (e *Event) Equals(ev *Event) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Len calculates the length of the string representation of event. Note that
|
||||
// this will return the true length (even if longer than what IRC supports),
|
||||
// which may be useful if you are trying to check and see if a message is
|
||||
// too long, to trim it down yourself.
|
||||
// split will split a potentially large event that is larger than what the server
|
||||
// supports, into multiple events. split will ignore events that cannot be split, and
|
||||
// if the event isn't longer than what the server supports, it will just return an array
|
||||
// with 1 entry, the original event.
|
||||
func (e *Event) split(maxLength int) []*Event {
|
||||
if len(e.Params) < 1 || (e.Command != PRIVMSG && e.Command != NOTICE) {
|
||||
return []*Event{e}
|
||||
}
|
||||
|
||||
// Exclude source, even if it does exist, because the server will likely ignore the
|
||||
// sent source anyway.
|
||||
event := e.Copy()
|
||||
event.Source = nil
|
||||
|
||||
if event.LenOpts(false) < maxLength {
|
||||
return []*Event{e}
|
||||
}
|
||||
|
||||
results := []*Event{}
|
||||
|
||||
// Will force the length check to include " :". This will allow us to get the length
|
||||
// of the commands and necessary prefixes.
|
||||
text := event.Last()
|
||||
event.Params[len(event.Params)-1] = ""
|
||||
cmdLen := event.LenOpts(false)
|
||||
|
||||
var ok bool
|
||||
var ctcp *CTCPEvent
|
||||
if ok, ctcp = e.IsCTCP(); ok {
|
||||
if len(text) == 0 {
|
||||
return []*Event{e}
|
||||
}
|
||||
|
||||
text = ctcp.Text
|
||||
|
||||
// ctcpDelim's at start and end, and space between command and trailing text.
|
||||
maxLength -= len(ctcp.Command) + 4
|
||||
}
|
||||
|
||||
// TODO: colors? use last color at start of split? make sure it's POST-color gen?
|
||||
|
||||
// If the command itself is longer than the limit, there is a problem. PRIVMSG should
|
||||
// be 1->1 per RFC. Just return the original message and let it be the user of the
|
||||
// libraries problem.
|
||||
if cmdLen > maxLength {
|
||||
return []*Event{e}
|
||||
}
|
||||
|
||||
// Split the text into correctly size segments, and make the necessary number of
|
||||
// events that duplicate the original event.
|
||||
for _, split := range splitAtWord(text, maxLength-cmdLen) {
|
||||
if ctcp != nil {
|
||||
split = string(ctcpDelim) + ctcp.Command + string(eventSpace) + split + string(ctcpDelim)
|
||||
}
|
||||
clonedEvent := event.Copy()
|
||||
clonedEvent.Source = e.Source
|
||||
clonedEvent.Params[len(e.Params)-1] = split
|
||||
results = append(results, clonedEvent)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// Len calculates the length of the string representation of event (including tags).
|
||||
// Note that this will return the true length (even if longer than what IRC supports),
|
||||
// which may be useful if you are trying to check and see if a message is too long, to
|
||||
// trim it down yourself.
|
||||
func (e *Event) Len() (length int) {
|
||||
return e.LenOpts(true)
|
||||
}
|
||||
|
||||
// LenOpts calculates the length of the string representation of event (with a toggle
|
||||
// for tags). Note that this will return the true length (even if longer than what IRC
|
||||
// supports), which may be useful if you are trying to check and see if a message is
|
||||
// too long, to trim it down yourself.
|
||||
func (e *Event) LenOpts(includeTags bool) (length int) {
|
||||
if e.Tags != nil {
|
||||
// Include tags and trailing space.
|
||||
length = e.Tags.Len() + 1
|
||||
@ -248,7 +353,7 @@ func (e *Event) Len() (length int) {
|
||||
|
||||
// If param contains a space or it's empty, it's trailing, so it should be
|
||||
// prefixed with a colon (:).
|
||||
if i == len(e.Params)-1 && (strings.Contains(e.Params[i], " ") || e.Params[i] == "") {
|
||||
if i == len(e.Params)-1 && (strings.Contains(e.Params[i], " ") || e.Params[i] == "" || strings.HasPrefix(e.Params[i], ":")) {
|
||||
length++
|
||||
}
|
||||
}
|
||||
@ -259,10 +364,6 @@ func (e *Event) Len() (length int) {
|
||||
|
||||
// Bytes returns a []byte representation of event. Strips all newlines and
|
||||
// carriage returns.
|
||||
//
|
||||
// Per RFC2812 section 2.3, messages should not exceed 512 characters in
|
||||
// length. This method forces that limit by discarding any characters
|
||||
// exceeding the length limit.
|
||||
func (e *Event) Bytes() []byte {
|
||||
buffer := new(bytes.Buffer)
|
||||
|
||||
@ -283,10 +384,8 @@ func (e *Event) Bytes() []byte {
|
||||
|
||||
// Space separated list of arguments.
|
||||
if len(e.Params) > 0 {
|
||||
// buffer.WriteByte(eventSpace)
|
||||
|
||||
for i := 0; i < len(e.Params); i++ {
|
||||
if i == len(e.Params)-1 && (strings.Contains(e.Params[i], " ") || e.Params[i] == "") {
|
||||
if i == len(e.Params)-1 && (strings.Contains(e.Params[i], " ") || e.Params[i] == "" || strings.HasPrefix(e.Params[i], ":")) {
|
||||
buffer.WriteString(string(eventSpace) + string(messagePrefix) + e.Params[i])
|
||||
continue
|
||||
}
|
||||
@ -294,11 +393,6 @@ func (e *Event) Bytes() []byte {
|
||||
}
|
||||
}
|
||||
|
||||
// We need the limit the buffer length.
|
||||
if buffer.Len() > (maxLength) {
|
||||
buffer.Truncate(maxLength)
|
||||
}
|
||||
|
||||
out := buffer.Bytes()
|
||||
|
||||
// Strip newlines and carriage returns.
|
||||
|
77
format.go
77
format.go
@ -350,3 +350,80 @@ func Glob(input, match string) bool {
|
||||
// Check suffix last.
|
||||
return trailingGlob || strings.HasSuffix(input, parts[last])
|
||||
}
|
||||
|
||||
const maxWordSplitLength = 30
|
||||
|
||||
// splitAtWord is a text splitter that takes into consideration a few things:
|
||||
// * Ensuring the returned text is no longer than maxWidth.
|
||||
// * Attempting to split at the closest word boundary, while still staying inside
|
||||
// of the specific maxWidth.
|
||||
// * if there is no good word boundry for longer words (or e.g. links, raw data, etc)
|
||||
// that are above maxWordSplitLength characters, split the word into chunks to fit the
|
||||
// maximum width.
|
||||
func splitAtWord(input string, maxWidth int) (output []string) {
|
||||
// TODO: breaks multi-spaces.
|
||||
words := strings.Fields(input)
|
||||
|
||||
// TODO: don't split a url if there isn't enough space, if it can safely fit within
|
||||
// the next line.
|
||||
// TODO: also split on newline, if splitting is enabled? makes it easier to just
|
||||
// pipe text in.
|
||||
// TODO: if word contains a dash, and adding the word to the line causes an overflow,
|
||||
// try to split on the dash?
|
||||
|
||||
// Increment maxWidth for calculations, because we always prefix with a space (then
|
||||
// strip it before we return).
|
||||
maxWidth++
|
||||
|
||||
var i, spaceRemaining int
|
||||
var split string
|
||||
|
||||
setupNextSplit:
|
||||
spaceRemaining = maxWidth
|
||||
split = ""
|
||||
beginLoop:
|
||||
for i < len(words) {
|
||||
// Last line was the perfect length, add to output and keep looping.
|
||||
if spaceRemaining == 0 {
|
||||
output = append(output, split[1:])
|
||||
goto setupNextSplit
|
||||
}
|
||||
|
||||
// Word makes the line too long.
|
||||
if len(words[i])+1 > spaceRemaining {
|
||||
// Is the word small enough to where we don't need to split it up?
|
||||
if len(words[i]) < maxWordSplitLength && maxWidth >= maxWordSplitLength {
|
||||
output = append(output, split[1:])
|
||||
goto setupNextSplit
|
||||
}
|
||||
|
||||
split += " " + words[i][0:spaceRemaining-1]
|
||||
if len(words) == i {
|
||||
words = append(words, words[i][spaceRemaining-1:])
|
||||
} else {
|
||||
words = append(words[:i+1], words[i:]...)
|
||||
words[i+1] = words[i][spaceRemaining-1:]
|
||||
words[i] = words[i][0 : spaceRemaining-1]
|
||||
}
|
||||
spaceRemaining -= 1 + len(words[i][0:spaceRemaining-1])
|
||||
i++
|
||||
goto beginLoop
|
||||
}
|
||||
|
||||
split += " " + words[i]
|
||||
spaceRemaining -= 1 + len(words[i])
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
if len(split) > 0 {
|
||||
output = append(output, split[1:])
|
||||
}
|
||||
|
||||
// At least return some kind of string, rather than nil.
|
||||
if len(output) == 0 {
|
||||
output = append(output, "")
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
15
state.go
15
state.go
@ -28,11 +28,22 @@ type state struct {
|
||||
// last capability check. These will get sent once we have received the
|
||||
// last capability list command from the server.
|
||||
tmpCap map[string]map[string]string
|
||||
|
||||
// serverOptions are the standard capabilities and configurations
|
||||
// supported by the server at connection time. This also includes
|
||||
// RPL_ISUPPORT entries.
|
||||
serverOptions map[string]string
|
||||
// motd is the servers message of the day.
|
||||
|
||||
// maxLineLength defines how long before we truncate (or split) messages.
|
||||
// DefaultMaxLineLength is what is used by default, as this is going to be a common
|
||||
// standard. However, protocols like IRCv3, or ISUPPORT can override this.
|
||||
maxLineLength int
|
||||
|
||||
// maxPrefixLength defines the estimated prefix length (":nick!user@host ") that
|
||||
// we can use to calculate line splits.
|
||||
maxPrefixLength int
|
||||
|
||||
motd string
|
||||
|
||||
// sts are strict transport security configurations, if specified by the
|
||||
@ -51,9 +62,11 @@ func (s *state) reset(initial bool) {
|
||||
s.host = ""
|
||||
s.channels = make(map[string]*Channel)
|
||||
s.users = make(map[string]*User)
|
||||
s.serverOptions = make(map[string]string)
|
||||
s.enabledCap = make(map[string]map[string]string)
|
||||
s.tmpCap = make(map[string]map[string]string)
|
||||
s.serverOptions = make(map[string]string)
|
||||
s.maxLineLength = DefaultMaxLineLength
|
||||
s.maxPrefixLength = DefaultMaxPrefixLength
|
||||
s.motd = ""
|
||||
|
||||
if initial {
|
||||
|
Loading…
Reference in New Issue
Block a user