1// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net> 2// released under the MIT license 3 4package irc 5 6import ( 7 "fmt" 8 "strings" 9 10 "github.com/ergochat/ergo/irc/history" 11 "github.com/ergochat/ergo/irc/modes" 12 "github.com/ergochat/ergo/irc/utils" 13) 14 15const ( 16 npcNickMask = "*%s*!%s@npc.fakeuser.invalid" 17 sceneNickMask = "=Scene=!%s@npc.fakeuser.invalid" 18) 19 20func sendRoleplayMessage(server *Server, client *Client, source string, targetString string, isScene, isAction bool, messageParts []string, rb *ResponseBuffer) { 21 config := server.Config() 22 if !config.Roleplay.Enabled { 23 rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Roleplaying has been disabled by the server administrators")) 24 return 25 } 26 if config.Roleplay.RequireOper && !client.HasRoleCapabs("roleplay") { 27 rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Insufficient privileges")) 28 return 29 } 30 31 var sourceMask string 32 if isScene { 33 sourceMask = fmt.Sprintf(sceneNickMask, client.Nick()) 34 } else { 35 cfSource, cfSourceErr := CasefoldName(source) 36 skelSource, skelErr := Skeleton(source) 37 if cfSourceErr != nil || skelErr != nil || 38 restrictedCasefoldedNicks.Has(cfSource) || restrictedSkeletons.Has(skelSource) { 39 rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Invalid roleplay name")) 40 return 41 } 42 sourceMask = fmt.Sprintf(npcNickMask, source, client.Nick()) 43 } 44 45 // block attempts to send CTCP messages to Tor clients 46 if len(messageParts) > 0 && len(messageParts[0]) > 0 && messageParts[0][0] == '\x01' { 47 return 48 } 49 50 var buf strings.Builder 51 if isAction { 52 buf.WriteString("\x01ACTION ") 53 } 54 for i, part := range messageParts { 55 buf.WriteString(part) 56 if i != len(messageParts)-1 { 57 buf.WriteByte(' ') 58 } 59 } 60 if config.Roleplay.addSuffix { 61 buf.WriteString(" (") 62 buf.WriteString(client.Nick()) 63 buf.WriteString(")") 64 } 65 if isAction { 66 buf.WriteString("\x01") 67 } 68 69 splitMessage := utils.MakeMessage(buf.String()) 70 71 target, cerr := CasefoldChannel(targetString) 72 if cerr == nil { 73 channel := server.channels.Get(target) 74 if channel == nil { 75 rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, targetString, client.t("No such channel")) 76 return 77 } 78 79 targetString = channel.Name() 80 if canSpeak, mode := channel.CanSpeak(client); !canSpeak { 81 rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, targetString, fmt.Sprintf(client.t("Cannot send to channel (+%s)"), mode)) 82 return 83 } 84 85 if !channel.flags.HasMode(modes.ChanRoleplaying) { 86 rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Channel doesn't have roleplaying mode available")) 87 return 88 } 89 90 if config.Roleplay.RequireChanops && !channel.ClientIsAtLeast(client, modes.ChannelOperator) { 91 rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Insufficient privileges")) 92 return 93 } 94 95 isBot := client.HasMode(modes.Bot) 96 for _, member := range channel.Members() { 97 for _, session := range member.Sessions() { 98 // see discussion on #865: clients do not understand how to do local echo 99 // of roleplay commands, so send them a copy whether they have echo-message 100 // or not 101 if rb.session == session { 102 rb.AddSplitMessageFromClient(sourceMask, "*", isBot, nil, "PRIVMSG", targetString, splitMessage) 103 } else { 104 session.sendSplitMsgFromClientInternal(false, sourceMask, "*", isBot, nil, "PRIVMSG", targetString, splitMessage) 105 } 106 } 107 } 108 109 channel.AddHistoryItem(history.Item{ 110 Type: history.Privmsg, 111 Message: splitMessage, 112 Nick: sourceMask, 113 }, client.Account()) 114 } else { 115 target, err := CasefoldName(targetString) 116 user := server.clients.Get(target) 117 if err != nil || user == nil { 118 rb.Add(nil, server.name, ERR_NOSUCHNICK, client.nick, target, client.t("No such nick")) 119 return 120 } 121 122 if !user.HasMode(modes.UserRoleplaying) { 123 rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, user.nick, client.t("User doesn't have roleplaying mode enabled")) 124 return 125 } 126 127 cnick := client.Nick() 128 tnick := user.Nick() 129 isBot := client.HasMode(modes.Bot) 130 for _, session := range user.Sessions() { 131 session.sendSplitMsgFromClientInternal(false, sourceMask, "*", isBot, nil, "PRIVMSG", tnick, splitMessage) 132 } 133 if away, awayMessage := user.Away(); away { 134 //TODO(dan): possibly implement cooldown of away notifications to users 135 rb.Add(nil, server.name, RPL_AWAY, cnick, tnick, awayMessage) 136 } 137 } 138} 139