mirror of https://github.com/fluffle/goirc
Split long messages at a configurable length. Fixes #29.
This commit is contained in:
parent
0b64613fe3
commit
5c56572b0d
|
@ -38,6 +38,46 @@ func cutNewLines(s string) string {
|
|||
return r[0]
|
||||
}
|
||||
|
||||
// indexFragment looks for the last sentence split-point (defined as one of
|
||||
// the punctuation characters .:;,!?"' followed by a space) in the string s
|
||||
// and returns the index in the string after that split-point. If no split-
|
||||
// point is found it returns the index after the last space in s, or -1.
|
||||
func indexFragment(s string) int {
|
||||
max := -1
|
||||
for _, sep := range []string{". ", ": ", "; ", ", ", "! ", "? ", "\" ", "' "} {
|
||||
if idx := strings.LastIndex(s, sep); idx > max {
|
||||
max = idx
|
||||
}
|
||||
}
|
||||
if max > 0 {
|
||||
return max + 2
|
||||
}
|
||||
if idx := strings.LastIndex(s, " "); idx > 0 {
|
||||
return idx + 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// splitMessage splits a message > splitLen chars at:
|
||||
// 1. the end of the last sentence fragment before splitLen
|
||||
// 2. the end of the last word before splitLen
|
||||
// 3. splitLen itself
|
||||
func splitMessage(msg string, splitLen int) (msgs []string) {
|
||||
// This is quite short ;-)
|
||||
if splitLen < 10 {
|
||||
splitLen = 10
|
||||
}
|
||||
for len(msg) > splitLen {
|
||||
idx := indexFragment(msg[:splitLen])
|
||||
if idx < 0 {
|
||||
idx = splitLen
|
||||
}
|
||||
msgs = append(msgs, msg[:idx] + "...")
|
||||
msg = msg[idx:]
|
||||
}
|
||||
return append(msgs, msg)
|
||||
}
|
||||
|
||||
// Raw() sends a raw line to the server, should really only be used for
|
||||
// debugging purposes but may well come in handy.
|
||||
func (conn *Conn) Raw(rawline string) {
|
||||
|
@ -93,29 +133,42 @@ func (conn *Conn) Whois(nick string) { conn.Raw(WHOIS + " " + nick) }
|
|||
func (conn *Conn) Who(nick string) { conn.Raw(WHO + " " + nick) }
|
||||
|
||||
// Privmsg() sends a PRIVMSG to the target t
|
||||
func (conn *Conn) Privmsg(t, msg string) { conn.Raw(PRIVMSG + " " + t + " :" + msg) }
|
||||
func (conn *Conn) Privmsg(t, msg string) {
|
||||
for _, s := range splitMessage(msg, conn.cfg.SplitLen) {
|
||||
conn.Raw(PRIVMSG + " " + t + " :" + s)
|
||||
}
|
||||
}
|
||||
|
||||
// Notice() sends a NOTICE to the target t
|
||||
func (conn *Conn) Notice(t, msg string) { conn.Raw(NOTICE + " " + t + " :" + msg) }
|
||||
func (conn *Conn) Notice(t, msg string) {
|
||||
for _, s := range splitMessage(msg, conn.cfg.SplitLen) {
|
||||
conn.Raw(NOTICE + " " + t + " :" + s)
|
||||
}
|
||||
}
|
||||
|
||||
// Ctcp() sends a (generic) CTCP message to the target t
|
||||
// with an optional argument
|
||||
func (conn *Conn) Ctcp(t, ctcp string, arg ...string) {
|
||||
msg := strings.Join(arg, " ")
|
||||
if msg != "" {
|
||||
msg = " " + msg
|
||||
// We need to split again here to ensure
|
||||
for _, s := range splitMessage(strings.Join(arg, " "), conn.cfg.SplitLen) {
|
||||
if s != "" {
|
||||
s = " " + s
|
||||
}
|
||||
// Using Raw rather than PRIVMSG here to avoid double-split problems.
|
||||
conn.Raw(PRIVMSG + " " + t + " :\001" + strings.ToUpper(ctcp) + s + "\001")
|
||||
}
|
||||
conn.Privmsg(t, "\001"+strings.ToUpper(ctcp)+msg+"\001")
|
||||
}
|
||||
|
||||
// CtcpReply() sends a generic CTCP reply to the target t
|
||||
// with an optional argument
|
||||
func (conn *Conn) CtcpReply(t, ctcp string, arg ...string) {
|
||||
msg := strings.Join(arg, " ")
|
||||
if msg != "" {
|
||||
msg = " " + msg
|
||||
for _, s := range splitMessage(strings.Join(arg, " "), conn.cfg.SplitLen) {
|
||||
if s != "" {
|
||||
s = " " + s
|
||||
}
|
||||
// Using Raw rather than NOTICE here to avoid double-split problems.
|
||||
conn.Raw(NOTICE + " " + t + " :\001" + strings.ToUpper(ctcp) + s + "\001")
|
||||
}
|
||||
conn.Notice(t, "\001"+strings.ToUpper(ctcp)+msg+"\001")
|
||||
}
|
||||
|
||||
// Version() sends a CTCP "VERSION" to the target t
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package client
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCutNewLines(t *testing.T) {
|
||||
tests := []struct{ in, out string }{
|
||||
|
@ -16,7 +19,58 @@ func TestCutNewLines(t *testing.T) {
|
|||
for i, test := range tests {
|
||||
out := cutNewLines(test.in)
|
||||
if test.out != out {
|
||||
t.Errorf("test %d: expected '%s', got '%s'", i, test.out, out)
|
||||
t.Errorf("test %d: expected %q, got %q", i, test.out, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexFragment(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
out int
|
||||
}{
|
||||
{"", -1},
|
||||
{"foobarbaz", -1},
|
||||
{"foo bar baz", 8},
|
||||
{"foo. bar baz", 5},
|
||||
{"foo: bar baz", 5},
|
||||
{"foo; bar baz", 5},
|
||||
{"foo, bar baz", 5},
|
||||
{"foo! bar baz", 5},
|
||||
{"foo? bar baz", 5},
|
||||
{"foo\" bar baz", 5},
|
||||
{"foo' bar baz", 5},
|
||||
{"foo. bar. baz beep", 10},
|
||||
{"foo. bar, baz beep", 10},
|
||||
}
|
||||
for i, test := range tests {
|
||||
out := indexFragment(test.in)
|
||||
if test.out != out {
|
||||
t.Errorf("test %d: expected %d, got %d", i, test.out, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitMessage(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
sp int
|
||||
out []string
|
||||
}{
|
||||
{"", 0, []string{""}},
|
||||
{"foo", 0, []string{"foo"}},
|
||||
{"foo bar baz beep", 0, []string{"foo bar ...", "baz beep"}},
|
||||
{"foo bar baz beep", 13, []string{"foo bar baz ...", "beep"}},
|
||||
{"foo. bar baz beep", 0, []string{"foo. ...", "bar baz ...", "beep"}},
|
||||
{"foo bar, baz beep", 13, []string{"foo bar, ...", "baz beep"}},
|
||||
{"0123456789012345", 0, []string{"0123456789...", "012345"}},
|
||||
{"0123456789012345", 13, []string{"0123456789012...", "345"}},
|
||||
{"0123456789012345", 20, []string{"0123456789012345"}},
|
||||
}
|
||||
for i, test := range tests {
|
||||
out := splitMessage(test.in, test.sp)
|
||||
if !reflect.DeepEqual(test.out, out) {
|
||||
t.Errorf("test %d: expected %q, got %q", i, test.out, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +79,10 @@ func TestClientCommands(t *testing.T) {
|
|||
c, s := setUp(t)
|
||||
defer s.tearDown()
|
||||
|
||||
// Avoid having to type ridiculously long lines to test that
|
||||
// messages longer than SplitLen are correctly sent to the server.
|
||||
c.cfg.SplitLen = 20
|
||||
|
||||
c.Pass("password")
|
||||
s.nc.Expect("PASS password")
|
||||
|
||||
|
@ -59,12 +117,30 @@ func TestClientCommands(t *testing.T) {
|
|||
c.Privmsg("#foo", "bar")
|
||||
s.nc.Expect("PRIVMSG #foo :bar")
|
||||
|
||||
// 0123456789012345678901234567890123
|
||||
c.Privmsg("#foo", "foo bar baz blorp. woo woobly woo.")
|
||||
s.nc.Expect("PRIVMSG #foo :foo bar baz blorp. ...")
|
||||
s.nc.Expect("PRIVMSG #foo :woo woobly woo.")
|
||||
|
||||
c.Notice("somebody", "something")
|
||||
s.nc.Expect("NOTICE somebody :something")
|
||||
|
||||
// 01234567890123456789012345678901234567
|
||||
c.Notice("somebody", "something much much longer that splits")
|
||||
s.nc.Expect("NOTICE somebody :something much much ...")
|
||||
s.nc.Expect("NOTICE somebody :longer that splits")
|
||||
|
||||
c.Ctcp("somebody", "ping", "123456789")
|
||||
s.nc.Expect("PRIVMSG somebody :\001PING 123456789\001")
|
||||
|
||||
c.Ctcp("somebody", "ping", "123456789012345678901234567890")
|
||||
s.nc.Expect("PRIVMSG somebody :\001PING 12345678901234567890...\001")
|
||||
s.nc.Expect("PRIVMSG somebody :\001PING 1234567890\001")
|
||||
|
||||
c.CtcpReply("somebody", "pong", "123456789012345678901234567890")
|
||||
s.nc.Expect("NOTICE somebody :\001PONG 12345678901234567890...\001")
|
||||
s.nc.Expect("NOTICE somebody :\001PONG 1234567890\001")
|
||||
|
||||
c.CtcpReply("somebody", "pong", "123456789")
|
||||
s.nc.Expect("NOTICE somebody :\001PONG 123456789\001")
|
||||
|
||||
|
|
|
@ -71,6 +71,10 @@ type Config struct {
|
|||
|
||||
// Configurable panic recovery for all handlers.
|
||||
Recover func(*Conn, *Line)
|
||||
|
||||
// Split PRIVMSGs, NOTICEs and CTCPs longer than
|
||||
// SplitLen characters over multiple lines.
|
||||
SplitLen int
|
||||
}
|
||||
|
||||
func NewConfig(nick string, args ...string) *Config {
|
||||
|
@ -79,6 +83,7 @@ func NewConfig(nick string, args ...string) *Config {
|
|||
PingFreq: 3 * time.Minute,
|
||||
NewNick: func(s string) string { return s + "_" },
|
||||
Recover: (*Conn).LogPanic, // in dispatch.go
|
||||
SplitLen: 450,
|
||||
}
|
||||
cfg.Me.Ident = "goirc"
|
||||
if len(args) > 0 && args[0] != "" {
|
||||
|
|
Loading…
Reference in New Issue