diff --git a/client/line.go b/client/line.go index f8ff82a..da2e708 100644 --- a/client/line.go +++ b/client/line.go @@ -25,6 +25,44 @@ func (l *Line) Copy() *Line { return &nl } +// Return the contents of the text portion of a line. This only really +// makes sense for lines with a :text part, but there are a lot of them. +func (line *Line) Text() string { + if len(line.Args) > 0 { + return line.Args[len(line.Args)-1] + } + return "" +} + +// Return the target of the line, usually the first Arg for the IRC verb. +// If the line was broadcast from a channel, the target will be that channel. +// If the line was broadcast by a user, the target will be that user. +// NOTE: Makes the assumption that all channels start with #. +// TODO(fluffle): Add 005 CHANTYPES parsing for this? +func (line *Line) Target() string { + switch line.Cmd { + case PRIVMSG, NOTICE, ACTION: + if !strings.HasPrefix(line.Args[0], "#") { + return line.Nick + } + case CTCP, CTCPREPLY: + // CTCP prepends the CTCP verb to line.Args, thus for the message + // :nick!user@host PRIVMSG #foo :\001BAR baz\001 + // line.Args contains: []string{"BAR", "#foo", "baz"} + // TODO(fluffle): Arguably this is broken, and we should have + // line.Args containing: []string{"#foo", "BAR", "baz"} + // ... OR change conn.Ctcp()'s argument order to be consistent. + if !strings.HasPrefix(line.Args[1], "#") { + return line.Nick + } + return line.Args[1] + } + if len(line.Args) > 0 { + return line.Args[0] + } + return "" +} + // parseLine() creates a Line from an incoming message from the IRC server. func parseLine(s string) *Line { line := &Line{Raw: s} diff --git a/client/line_test.go b/client/line_test.go index 7ae2db5..61370f8 100644 --- a/client/line_test.go +++ b/client/line_test.go @@ -42,3 +42,43 @@ func TestLineCopy(t *testing.T) { t.Errorf("l1: %#v\nl2: %#v", l1, l2) } } + +func TestLineText(t *testing.T) { + tests := []struct{in *Line; out string}{ + {&Line{}, ""}, + {&Line{Args: []string{"one thing"}}, "one thing"}, + {&Line{Args: []string{"one", "two"}}, "two"}, + } + + for i, test := range tests { + out := test.in.Text() + if out != test.out { + t.Errorf("test %d: expected: '%s', got '%s'", i, test.out, out) + } + } +} + +func TestLineTarget(t *testing.T) { + tests := []struct{in *Line; out string}{ + {&Line{}, ""}, + {&Line{Cmd: JOIN, Args: []string{"#foo"}}, "#foo"}, + {&Line{Cmd: PART, Args: []string{"#foo", "bye"}}, "#foo"}, + {&Line{Cmd: PRIVMSG, Args: []string{"Me", "la"}, Nick: "Them"}, "Them"}, + {&Line{Cmd: NOTICE, Args: []string{"Me", "la"}, Nick: "Them"}, "Them"}, + {&Line{Cmd: ACTION, Args: []string{"Me", "la"}, Nick: "Them"}, "Them"}, + {&Line{Cmd: CTCP, Args: []string{"PING", "Me", "1"}, Nick: "Them"}, "Them"}, + {&Line{Cmd: CTCPREPLY, Args: []string{"PONG", "Me", "2"}, Nick: "Them"}, "Them"}, + {&Line{Cmd: PRIVMSG, Args: []string{"#foo", "la"}, Nick: "Them"}, "#foo"}, + {&Line{Cmd: NOTICE, Args: []string{"#foo", "la"}, Nick: "Them"}, "#foo"}, + {&Line{Cmd: ACTION, Args: []string{"#foo", "la"}, Nick: "Them"}, "#foo"}, + {&Line{Cmd: CTCP, Args: []string{"PING", "#foo", "1"}, Nick: "Them"}, "#foo"}, + {&Line{Cmd: CTCPREPLY, Args: []string{"PONG", "#foo", "2"}, Nick: "Them"}, "#foo"}, + } + + for i, test := range tests { + out := test.in.Target() + if out != test.out { + t.Errorf("test %d: expected: '%s', got '%s'", i, test.out, out) + } + } +}