diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fb89c64..9a0fd3ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ Improved compatibility, more features, etc. ### Added * Added integrated help. -* Support for IRCv3 capability [`account-notify`](http://ircv3.net/specs/extensions/account-notify-3.1.html) +* Support for IRCv3 capability [`account-notify`](http://ircv3.net/specs/extensions/account-notify-3.1.html), and draft capability [`message-tags`](http://ircv3.net/specs/core/message-tags-3.3.html) as `draft/message-tags`. ### Changed * Casemapping changed from custom unicode mapping to preliminary [rfc7700](https://github.com/ircv3/ircv3-specifications/pull/272) mapping. diff --git a/irc/capability.go b/irc/capability.go index 6c3a127b..cf3f56a3 100644 --- a/irc/capability.go +++ b/irc/capability.go @@ -18,6 +18,7 @@ const ( AccountNotify Capability = "account-notify" AwayNotify Capability = "away-notify" ExtendedJoin Capability = "extended-join" + MessageTags Capability = "draft/message-tags" MultiPrefix Capability = "multi-prefix" SASL Capability = "sasl" ServerTime Capability = "server-time" @@ -30,6 +31,7 @@ var ( AccountNotify: true, AwayNotify: true, ExtendedJoin: true, + MessageTags: true, MultiPrefix: true, SASL: true, ServerTime: true, diff --git a/irc/channel.go b/irc/channel.go index ca9ef842..80e4c9f4 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -10,6 +10,8 @@ import ( "log" "strconv" "time" + + "github.com/DanielOaks/girc-go/ircmsg" ) type Channel struct { @@ -300,7 +302,7 @@ func (channel *Channel) CanSpeak(client *Client) bool { return true } -func (channel *Channel) PrivMsg(client *Client, message string) { +func (channel *Channel) PrivMsg(clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message string) { if !channel.CanSpeak(client) { client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, "Cannot send to channel") return @@ -309,7 +311,11 @@ func (channel *Channel) PrivMsg(client *Client, message string) { if member == client { continue } - member.SendFromClient(client, nil, client.nickMaskString, "PRIVMSG", channel.name, message) + if member.capabilities[MessageTags] { + member.SendFromClient(client, clientOnlyTags, client.nickMaskString, "PRIVMSG", channel.name, message) + } else { + member.SendFromClient(client, nil, client.nickMaskString, "PRIVMSG", channel.name, message) + } } } diff --git a/irc/message_tags.go b/irc/message_tags.go new file mode 100644 index 00000000..7ef5496e --- /dev/null +++ b/irc/message_tags.go @@ -0,0 +1,23 @@ +// Copyright (c) 2016- Daniel Oaks +// released under the MIT license + +package irc + +import "github.com/DanielOaks/girc-go/ircmsg" + +// GetClientOnlyTags tags a tag map, and returns a map containing just the client-only tags from it. +func GetClientOnlyTags(tags map[string]ircmsg.TagValue) *map[string]ircmsg.TagValue { + if len(tags) < 1 { + return nil + } + + clientOnlyTags := make(map[string]ircmsg.TagValue) + + for name, value := range tags { + if len(name) > 1 && name[0] == '+' { + clientOnlyTags[name] = value + } + } + + return &clientOnlyTags +} diff --git a/irc/server.go b/irc/server.go index cba4d76d..2613f7d2 100644 --- a/irc/server.go +++ b/irc/server.go @@ -91,13 +91,13 @@ func NewServer(config *Config) *Server { NickLen: config.Limits.NickLen, TopicLen: config.Limits.TopicLen, }, - name: config.Server.Name, - nameCasefolded: casefoldedName, - newConns: make(chan clientConn), - operators: config.Operators(), - signals: make(chan os.Signal, len(SERVER_SIGNALS)), - whoWas: NewWhoWasList(config.Limits.WhowasEntries), - checkIdent: config.Server.CheckIdent, + name: config.Server.Name, + nameCasefolded: casefoldedName, + newConns: make(chan clientConn), + operators: config.Operators(), + signals: make(chan os.Signal, len(SERVER_SIGNALS)), + whoWas: NewWhoWasList(config.Limits.WhowasEntries), + checkIdent: config.Server.CheckIdent, } // open data store @@ -567,6 +567,7 @@ func topicHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { // PRIVMSG {,} func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { + clientOnlyTags := GetClientOnlyTags(msg.Tags) targets := strings.Split(msg.Params[0], ",") message := msg.Params[1] @@ -578,7 +579,7 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, targetString, "No such channel") continue } - channel.PrivMsg(client, message) + channel.PrivMsg(clientOnlyTags, client, message) } else { target, err = CasefoldName(targetString) user := server.clients.Get(target) @@ -586,7 +587,10 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool client.Send(nil, server.name, ERR_NOSUCHNICK, target, "No such nick") continue } - user.Send(nil, client.nickMaskString, "PRIVMSG", user.nick, message) + if !user.capabilities[MessageTags] { + clientOnlyTags = nil + } + user.Send(clientOnlyTags, client.nickMaskString, "PRIVMSG", user.nick, message) if user.flags[Away] { //TODO(dan): possibly implement cooldown of away notifications to users client.Send(nil, server.name, RPL_AWAY, user.nick, user.awayMessage) @@ -852,6 +856,7 @@ func motdHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { // NOTICE {,} func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { + clientOnlyTags := GetClientOnlyTags(msg.Tags) targets := strings.Split(msg.Params[0], ",") message := msg.Params[1] @@ -863,7 +868,7 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { // errors silently ignored with NOTICE as per RFC continue } - channel.PrivMsg(client, message) + channel.PrivMsg(clientOnlyTags, client, message) } else { target, err := CasefoldName(targetString) if err != nil { @@ -875,7 +880,10 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { // errors silently ignored with NOTICE as per RFC continue } - user.Send(nil, client.nickMaskString, "NOTICE", user.nick, message) + if !user.capabilities[MessageTags] { + clientOnlyTags = nil + } + user.Send(clientOnlyTags, client.nickMaskString, "NOTICE", user.nick, message) } } return false