Add support for parsing IRCv3 tags in received messages

This commit is contained in:
Russ Garrett 2017-01-25 15:39:59 +00:00
parent 1b0acb5f2f
commit e1d00ae799
No known key found for this signature in database
GPG Key ID: 68880BB652AB0570
3 changed files with 79 additions and 0 deletions

31
irc.go
View File

@ -87,6 +87,17 @@ func (irc *Connection) readLoop() {
} }
} }
// Unescape tag values as defined in the IRCv3.2 message tags spec
// http://ircv3.net/specs/core/message-tags-3.2.html
func unescapeTagValue(value string) string {
value = strings.Replace(value, "\\:", ";", -1)
value = strings.Replace(value, "\\s", " ", -1)
value = strings.Replace(value, "\\\\", "\\", -1)
value = strings.Replace(value, "\\r", "\r", -1)
value = strings.Replace(value, "\\n", "\n", -1)
return value
}
//Parse raw irc messages //Parse raw irc messages
func parseToEvent(msg string) (*Event, error) { func parseToEvent(msg string) (*Event, error) {
msg = strings.TrimSuffix(msg, "\n") //Remove \r\n msg = strings.TrimSuffix(msg, "\n") //Remove \r\n
@ -95,6 +106,26 @@ func parseToEvent(msg string) (*Event, error) {
if len(msg) < 5 { if len(msg) < 5 {
return nil, errors.New("Malformed msg from server") return nil, errors.New("Malformed msg from server")
} }
if msg[0] == '@' {
// IRCv3 Message Tags
if i := strings.Index(msg, " "); i > -1 {
event.Tags = make(map[string]string)
tags := strings.Split(msg[1:i], ";")
for _, data := range tags {
parts := strings.SplitN(data, "=", 2)
if len(parts) == 1 {
event.Tags[parts[0]] = ""
} else {
event.Tags[parts[0]] = unescapeTagValue(parts[1])
}
}
msg = msg[i+1 : len(msg)]
} else {
return nil, errors.New("Malformed msg from server")
}
}
if msg[0] == ':' { if msg[0] == ':' {
if i := strings.Index(msg, " "); i > -1 { if i := strings.Index(msg, " "); i > -1 {
event.Source = msg[1:i] event.Source = msg[1:i]

47
irc_parse_test.go Normal file
View File

@ -0,0 +1,47 @@
package irc
import (
"fmt"
"testing"
)
func checkResult(t *testing.T, event *Event) {
if event.Nick != "nick" {
t.Fatal("Parse failed: nick")
}
if event.User != "~user" {
t.Fatal("Parse failed: user")
}
if event.Code != "PRIVMSG" {
t.Fatal("Parse failed: code")
}
if event.Arguments[0] != "#channel" {
t.Fatal("Parse failed: channel")
}
if event.Arguments[1] != "message text" {
t.Fatal("Parse failed: message")
}
}
func TestParse(t *testing.T) {
event, err := parseToEvent(":nick!~user@host PRIVMSG #channel :message text")
if err != nil {
t.Fatal("Parse PRIVMSG failed")
}
checkResult(t, event)
}
func TestParseTags(t *testing.T) {
event, err := parseToEvent("@tag;+tag2=raw+:=,escaped\\:\\s\\\\ :nick!~user@host PRIVMSG #channel :message text")
if err != nil {
t.Fatal("Parse PRIVMSG with tags failed")
}
checkResult(t, event)
fmt.Printf("%s", event.Tags)
if _, ok := event.Tags["tag"]; !ok {
t.Fatal("Parsing value-less tag failed")
}
if event.Tags["+tag2"] != "raw+:=,escaped; \\" {
t.Fatal("Parsing tag failed")
}
}

View File

@ -59,6 +59,7 @@ type Event struct {
Source string //<host> Source string //<host>
User string //<usr> User string //<usr>
Arguments []string Arguments []string
Tags map[string]string
Connection *Connection Connection *Connection
} }