diff --git a/irc/commands.go b/irc/commands.go index c9a4550c..22153041 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -966,7 +966,7 @@ func NewTheaterCommand(args []string) (Command, error) { return &TheaterActionCommand{ channel: NewName(args[1]), asNick: NewName(args[2]), - action: NewText(args[3]), + action: NewCTCPText(args[3]), }, nil } else { return nil, ErrParseCommand diff --git a/irc/reply.go b/irc/reply.go index 040aedbf..2c11bea2 100644 --- a/irc/reply.go +++ b/irc/reply.go @@ -99,6 +99,10 @@ func RplPrivMsg(source Identifiable, target Identifiable, message Text) string { return NewStringReply(source, PRIVMSG, "%s :%s", target.Nick(), message) } +func RplCTCPAction(source Identifiable, target Identifiable, action CTCPText) string { + return RplPrivMsg(source, target, NewText(fmt.Sprintf("\x01ACTION %s\x01", action))) +} + func RplNotice(source Identifiable, target Identifiable, message Text) string { return NewStringReply(source, NOTICE, "%s :%s", target.Nick(), message) } diff --git a/irc/strings.go b/irc/strings.go index dfd01fcb..b8cc9227 100644 --- a/irc/strings.go +++ b/irc/strings.go @@ -64,3 +64,12 @@ func NewText(str string) Text { func (text Text) String() string { return string(text) } + +// CTCPText is text suitable escaped for CTCP. +type CTCPText string + +var ctcpEscaper = strings.NewReplacer("\x00", "\x200", "\n", "\x20n", "\r", "\x20r") + +func NewCTCPText(str string) CTCPText { + return CTCPText(ctcpEscaper.Replace(str)) +} diff --git a/irc/theater.go b/irc/theater.go index 783c851b..36525048 100644 --- a/irc/theater.go +++ b/irc/theater.go @@ -51,9 +51,12 @@ func (m *TheaterIdentifyCommand) HandleServer(s *Server) { return } - if !channel.members.AnyHasMode(Theater) { - channel.members[client][Theater] = true + if channel.members.AnyHasMode(Theater) { + client.Reply(RplNotice(s, client, "someone else is +T in this channel")) + return } + + channel.members[client][Theater] = true } type TheaterPrivMsgCommand struct { @@ -69,6 +72,7 @@ func (cmd *TheaterPrivMsgCommand) String() string { } func (m *TheaterPrivMsgCommand) HandleServer(s *Server) { client := m.Client() + if !m.channel.IsChannel() { client.ErrNoSuchChannel(m.channel) return @@ -80,10 +84,13 @@ func (m *TheaterPrivMsgCommand) HandleServer(s *Server) { return } - if channel.members.HasMode(client, Theater) { - for member := range channel.members { - member.Reply(RplPrivMsg(TheaterClient(m.asNick), channel, m.message)) - } + if !channel.members.HasMode(client, Theater) { + client.Reply(RplNotice(s, client, "you are not +T")) + return + } + + for member := range channel.members { + member.Reply(RplPrivMsg(TheaterClient(m.asNick), channel, m.message)) } } @@ -91,7 +98,7 @@ type TheaterActionCommand struct { BaseCommand channel Name asNick Name - action Text + action CTCPText } func (cmd *TheaterActionCommand) String() string { @@ -100,7 +107,8 @@ func (cmd *TheaterActionCommand) String() string { func (m *TheaterActionCommand) HandleServer(s *Server) { client := m.Client() - if m.channel.IsChannel() { + + if !m.channel.IsChannel() { client.ErrNoSuchChannel(m.channel) return } @@ -111,9 +119,12 @@ func (m *TheaterActionCommand) HandleServer(s *Server) { return } - if channel.members.HasMode(client, Theater) { - for member := range channel.members { - member.Reply(RplPrivMsg(TheaterClient(m.asNick), channel, NewText(fmt.Sprintf("\001ACTION %s\001", m.action)))) - } + if !channel.members.HasMode(client, Theater) { + client.Reply(RplNotice(s, client, "you are not +T")) + return + } + + for member := range channel.members { + member.Reply(RplCTCPAction(TheaterClient(m.asNick), channel, m.action)) } } diff --git a/irc/types.go b/irc/types.go index db698254..dabe4c2b 100644 --- a/irc/types.go +++ b/irc/types.go @@ -85,7 +85,7 @@ func (members MemberSet) HasMode(member *Client, mode ChannelMode) bool { func (members MemberSet) AnyHasMode(mode ChannelMode) bool { for _, modes := range members { - if modes[Theater] { + if modes[mode] { return true } }