package irc import ( "strings" ) type Message struct { Tags map[string]string Prefix string Nick string Command string Params []string } func (m *Message) LastParam() string { if len(m.Params) > 0 { return m.Params[len(m.Params)-1] } return "" } type CTCP struct { Command string Params string } func (m *Message) ToCTCP() *CTCP { lp := m.LastParam() if len(lp) > 1 && lp[0] == 0x01 { parts := strings.SplitN(strings.Trim(lp, "\x01"), " ", 2) ctcp := CTCP{} if parts[0] != "" { ctcp.Command = parts[0] } else { return nil } if len(parts) == 2 { ctcp.Params = parts[1] } return &ctcp } return nil } func ParseMessage(line string) *Message { msg := Message{} if strings.HasPrefix(line, "@") { next := strings.Index(line, " ") if next == -1 { return nil } tags := strings.Split(line[1:next], ";") if len(tags) > 0 { msg.Tags = map[string]string{} for _, tag := range tags { key, val := splitParam(tag) if key == "" { continue } if val != "" { msg.Tags[key] = unescapeTag(val) } else { msg.Tags[key] = "" } } } for line[next+1] == ' ' { next++ } line = line[next+1:] } if strings.HasPrefix(line, ":") { next := strings.Index(line, " ") if next == -1 { return nil } msg.Prefix = line[1:next] if i := strings.Index(msg.Prefix, "!"); i > 0 { msg.Nick = msg.Prefix[:i] } else if i := strings.Index(msg.Prefix, "@"); i > 0 { msg.Nick = msg.Prefix[:i] } else { msg.Nick = msg.Prefix } line = line[next+1:] } cmdEnd := len(line) trailing := "" if i := strings.Index(line, " :"); i >= 0 { cmdEnd = i trailing = line[i+2:] } cmd := strings.Fields(line[:cmdEnd]) if len(cmd) == 0 { return nil } msg.Command = cmd[0] if len(cmd) > 1 { msg.Params = cmd[1:] } if cmdEnd != len(line) { msg.Params = append(msg.Params, trailing) } return &msg } func splitParam(param string) (string, string) { parts := strings.SplitN(param, "=", 2) if len(parts) == 2 { return parts[0], parts[1] } return parts[0], "" } var unescapeTagReplacer = strings.NewReplacer( "\\:", ";", "\\s", " ", "\\\\", "\\", "\\r", "\r", "\\n", "\n", ) func unescapeTag(s string) string { return unescapeTagReplacer.Replace(s) }