// Copyright (c) 2016-2017 Daniel Oaks // released under the MIT license package irc import ( "fmt" "strings" "github.com/ergochat/ergo/irc/history" "github.com/ergochat/ergo/irc/modes" "github.com/ergochat/ergo/irc/utils" ) const ( npcNickMask = "*%s*!%s@npc.fakeuser.invalid" sceneNickMask = "=Scene=!%s@npc.fakeuser.invalid" ) func sendRoleplayMessage(server *Server, client *Client, source string, targetString string, isScene, isAction bool, messageParts []string, rb *ResponseBuffer) { config := server.Config() if !config.Roleplay.Enabled { rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Roleplaying has been disabled by the server administrators")) return } if config.Roleplay.RequireOper && !client.HasRoleCapabs("roleplay") { rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Insufficient privileges")) return } var sourceMask string if isScene { sourceMask = fmt.Sprintf(sceneNickMask, client.Nick()) } else { cfSource, cfSourceErr := CasefoldName(source) skelSource, skelErr := Skeleton(source) if cfSourceErr != nil || skelErr != nil || restrictedCasefoldedNicks.Has(cfSource) || restrictedSkeletons.Has(skelSource) { rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Invalid roleplay name")) return } sourceMask = fmt.Sprintf(npcNickMask, source, client.Nick()) } // block attempts to send CTCP messages to Tor clients if len(messageParts) > 0 && len(messageParts[0]) > 0 && messageParts[0][0] == '\x01' { return } var buf strings.Builder if isAction { buf.WriteString("\x01ACTION ") } for i, part := range messageParts { buf.WriteString(part) if i != len(messageParts)-1 { buf.WriteByte(' ') } } if config.Roleplay.addSuffix { buf.WriteString(" (") buf.WriteString(client.Nick()) buf.WriteString(")") } if isAction { buf.WriteString("\x01") } splitMessage := utils.MakeMessage(buf.String()) target, cerr := CasefoldChannel(targetString) if cerr == nil { channel := server.channels.Get(target) if channel == nil { rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, targetString, client.t("No such channel")) return } targetString = channel.Name() if canSpeak, mode := channel.CanSpeak(client); !canSpeak { rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, targetString, fmt.Sprintf(client.t("Cannot send to channel (+%s)"), mode)) return } if !channel.flags.HasMode(modes.ChanRoleplaying) { rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Channel doesn't have roleplaying mode available")) return } if config.Roleplay.RequireChanops && !channel.ClientIsAtLeast(client, modes.ChannelOperator) { rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Insufficient privileges")) return } isBot := client.HasMode(modes.Bot) for _, member := range channel.Members() { for _, session := range member.Sessions() { // see discussion on #865: clients do not understand how to do local echo // of roleplay commands, so send them a copy whether they have echo-message // or not if rb.session == session { rb.AddSplitMessageFromClient(sourceMask, "*", isBot, nil, "PRIVMSG", targetString, splitMessage) } else { session.sendSplitMsgFromClientInternal(false, sourceMask, "*", isBot, nil, "PRIVMSG", targetString, splitMessage) } } } channel.AddHistoryItem(history.Item{ Type: history.Privmsg, Message: splitMessage, Nick: sourceMask, }, client.Account()) } else { target, err := CasefoldName(targetString) user := server.clients.Get(target) if err != nil || user == nil { rb.Add(nil, server.name, ERR_NOSUCHNICK, client.nick, target, client.t("No such nick")) return } if !user.HasMode(modes.UserRoleplaying) { rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, user.nick, client.t("User doesn't have roleplaying mode enabled")) return } cnick := client.Nick() tnick := user.Nick() isBot := client.HasMode(modes.Bot) for _, session := range user.Sessions() { session.sendSplitMsgFromClientInternal(false, sourceMask, "*", isBot, nil, "PRIVMSG", tnick, splitMessage) } if away, awayMessage := user.Away(); away { //TODO(dan): possibly implement cooldown of away notifications to users rb.Add(nil, server.name, RPL_AWAY, cnick, tnick, awayMessage) } } }