Browse Source

Merge pull request #881 from slingamn/issue865_roleplay.2

fix #865
tags/v2.1.0-rc1
Shivaram Lingamneni 4 years ago
parent
commit
37cd2e77c5
No account linked to committer's email address
5 changed files with 104 additions and 45 deletions
  1. 19
    20
      irc/config.go
  2. 4
    4
      irc/handlers.go
  3. 56
    21
      irc/roleplay.go
  4. 7
    0
      irc/utils/args.go
  5. 18
    0
      oragono.yaml

+ 19
- 20
irc/config.go View File

@@ -507,6 +507,15 @@ type Config struct {
507 507
 		Casemapping   Casemapping
508 508
 	}
509 509
 
510
+	Roleplay struct {
511
+		Enabled        *bool
512
+		enabled        bool
513
+		RequireChanops bool  `yaml:"require-chanops"`
514
+		RequireOper    bool  `yaml:"require-oper"`
515
+		AddSuffix      *bool `yaml:"add-suffix"`
516
+		addSuffix      bool
517
+	}
518
+
510 519
 	Languages struct {
511 520
 		Enabled bool
512 521
 		Path    string
@@ -844,12 +853,7 @@ func LoadConfig(filename string) (config *Config, err error) {
844 853
 	// set this even if STS is disabled
845 854
 	config.Server.capValues[caps.STS] = config.Server.STS.Value()
846 855
 
847
-	// lookup-hostnames defaults to true if unset
848
-	if config.Server.LookupHostnames != nil {
849
-		config.Server.lookupHostnames = *config.Server.LookupHostnames
850
-	} else {
851
-		config.Server.lookupHostnames = true
852
-	}
856
+	config.Server.lookupHostnames = utils.BoolDefaultTrue(config.Server.LookupHostnames)
853 857
 
854 858
 	// process webirc blocks
855 859
 	var newWebIRC []webircConfig
@@ -1014,12 +1018,7 @@ func LoadConfig(filename string) (config *Config, err error) {
1014 1018
 	}
1015 1019
 	config.Server.capValues[caps.Languages] = config.languageManager.CapValue()
1016 1020
 
1017
-	// RecoverFromErrors defaults to true
1018
-	if config.Debug.RecoverFromErrors != nil {
1019
-		config.Debug.recoverFromErrors = *config.Debug.RecoverFromErrors
1020
-	} else {
1021
-		config.Debug.recoverFromErrors = true
1022
-	}
1021
+	config.Debug.recoverFromErrors = utils.BoolDefaultTrue(config.Debug.RecoverFromErrors)
1023 1022
 
1024 1023
 	// process operator definitions, store them to config.operators
1025 1024
 	operclasses, err := config.OperatorClasses()
@@ -1053,12 +1052,7 @@ func LoadConfig(filename string) (config *Config, err error) {
1053 1052
 		config.Channels.Registration.MaxChannelsPerAccount = 15
1054 1053
 	}
1055 1054
 
1056
-	forceTrailingPtr := config.Server.Compatibility.ForceTrailing
1057
-	if forceTrailingPtr != nil {
1058
-		config.Server.Compatibility.forceTrailing = *forceTrailingPtr
1059
-	} else {
1060
-		config.Server.Compatibility.forceTrailing = true
1061
-	}
1055
+	config.Server.Compatibility.forceTrailing = utils.BoolDefaultTrue(config.Server.Compatibility.ForceTrailing)
1062 1056
 
1063 1057
 	config.loadMOTD()
1064 1058
 
@@ -1080,6 +1074,9 @@ func LoadConfig(filename string) (config *Config, err error) {
1080 1074
 		config.History.ZNCMax = config.History.ChathistoryMax
1081 1075
 	}
1082 1076
 
1077
+	config.Roleplay.enabled = utils.BoolDefaultTrue(config.Roleplay.Enabled)
1078
+	config.Roleplay.addSuffix = utils.BoolDefaultTrue(config.Roleplay.AddSuffix)
1079
+
1083 1080
 	config.Datastore.MySQL.ExpireTime = time.Duration(config.History.Restrictions.ExpireTime)
1084 1081
 
1085 1082
 	config.Server.Cloaks.Initialize()
@@ -1133,8 +1130,10 @@ func (config *Config) generateISupport() (err error) {
1133 1130
 	isupport.Add("NETWORK", config.Network.Name)
1134 1131
 	isupport.Add("NICKLEN", strconv.Itoa(config.Limits.NickLen))
1135 1132
 	isupport.Add("PREFIX", "(qaohv)~&@%+")
1136
-	isupport.Add("RPCHAN", "E")
1137
-	isupport.Add("RPUSER", "E")
1133
+	if config.Roleplay.enabled {
1134
+		isupport.Add("RPCHAN", "E")
1135
+		isupport.Add("RPUSER", "E")
1136
+	}
1138 1137
 	isupport.Add("STATUSMSG", "~&@%+")
1139 1138
 	isupport.Add("TARGMAX", fmt.Sprintf("NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:%s,TAGMSG:%s,NOTICE:%s,MONITOR:", maxTargetsString, maxTargetsString, maxTargetsString))
1140 1139
 	isupport.Add("TOPICLEN", strconv.Itoa(config.Limits.TopicLen))

+ 4
- 4
irc/handlers.go View File

@@ -2027,7 +2027,7 @@ func dispatchMessageToTarget(client *Client, tags map[string]string, histType hi
2027 2027
 func npcHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
2028 2028
 	target := msg.Params[0]
2029 2029
 	fakeSource := msg.Params[1]
2030
-	message := msg.Params[2]
2030
+	message := msg.Params[2:]
2031 2031
 
2032 2032
 	_, err := CasefoldName(fakeSource)
2033 2033
 	if err != nil {
@@ -2046,7 +2046,7 @@ func npcHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Respo
2046 2046
 func npcaHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
2047 2047
 	target := msg.Params[0]
2048 2048
 	fakeSource := msg.Params[1]
2049
-	message := msg.Params[2]
2049
+	message := msg.Params[2:]
2050 2050
 	sourceString := fmt.Sprintf(npcNickMask, fakeSource, client.nick)
2051 2051
 
2052 2052
 	_, err := CasefoldName(fakeSource)
@@ -2231,7 +2231,7 @@ func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
2231 2231
 		rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.Nick(), utils.SafeErrorParam(oldName), client.t("No such channel"))
2232 2232
 		return false
2233 2233
 	}
2234
-	if !(channel.ClientIsAtLeast(client, modes.Operator) || client.HasRoleCapabs("chanreg")) {
2234
+	if !(channel.ClientIsAtLeast(client, modes.ChannelOperator) || client.HasRoleCapabs("chanreg")) {
2235 2235
 		rb.Add(nil, server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), oldName, client.t("You're not a channel operator"))
2236 2236
 		return false
2237 2237
 	}
@@ -2334,7 +2334,7 @@ func sanickHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
2334 2334
 // SCENE <target> <message>
2335 2335
 func sceneHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
2336 2336
 	target := msg.Params[0]
2337
-	message := msg.Params[1]
2337
+	message := msg.Params[1:]
2338 2338
 	sourceString := fmt.Sprintf(sceneNickMask, client.nick)
2339 2339
 
2340 2340
 	sendRoleplayMessage(server, client, sourceString, target, false, message, rb)

+ 56
- 21
irc/roleplay.go View File

@@ -4,10 +4,11 @@
4 4
 package irc
5 5
 
6 6
 import (
7
-	"fmt"
7
+	"bytes"
8 8
 
9
-	"github.com/oragono/oragono/irc/caps"
9
+	"github.com/oragono/oragono/irc/history"
10 10
 	"github.com/oragono/oragono/irc/modes"
11
+	"github.com/oragono/oragono/irc/utils"
11 12
 )
12 13
 
13 14
 const (
@@ -15,18 +16,40 @@ const (
15 16
 	sceneNickMask = "=Scene=!%s@npc.fakeuser.invalid"
16 17
 )
17 18
 
18
-func sendRoleplayMessage(server *Server, client *Client, source string, targetString string, isAction bool, message string, rb *ResponseBuffer) {
19
+func sendRoleplayMessage(server *Server, client *Client, source string, targetString string, isAction bool, messageParts []string, rb *ResponseBuffer) {
20
+	config := server.Config()
21
+	if !config.Roleplay.enabled {
22
+		rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Roleplaying has been disabled by the server administrators"))
23
+		return
24
+	}
25
+	if config.Roleplay.RequireOper && !client.HasRoleCapabs("roleplay") {
26
+		rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Insufficient privileges"))
27
+		return
28
+	}
29
+
30
+	// block attempts to send CTCP messages to Tor clients
31
+	if len(messageParts) > 0 && len(messageParts[0]) > 0 && messageParts[0][0] == '\x01' {
32
+		return
33
+	}
34
+
35
+	var buf bytes.Buffer
19 36
 	if isAction {
20
-		message = fmt.Sprintf("\x01ACTION %s (%s)\x01", message, client.nick)
21
-	} else {
22
-		// block attempts to send CTCP messages to Tor clients
23
-		// TODO(#395) clean this up
24
-		if len(message) != 0 && message[0] == '\x01' {
25
-			return
37
+		buf.WriteString("\x01ACTION ")
38
+	}
39
+	for i, part := range messageParts {
40
+		buf.WriteString(part)
41
+		if i != len(messageParts)-1 {
42
+			buf.WriteByte(' ')
26 43
 		}
27
-		message = fmt.Sprintf("%s (%s)", message, client.nick)
44
+	}
45
+	if config.Roleplay.addSuffix {
46
+		buf.WriteString(" (")
47
+		buf.WriteString(client.Nick())
48
+		buf.WriteString(")")
28 49
 	}
29 50
 
51
+	splitMessage := utils.MakeMessage(buf.String())
52
+
30 53
 	target, cerr := CasefoldChannel(targetString)
31 54
 	if cerr == nil {
32 55
 		channel := server.channels.Get(target)
@@ -35,27 +58,40 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
35 58
 			return
36 59
 		}
37 60
 
61
+		targetString = channel.Name()
38 62
 		if !channel.CanSpeak(client) {
39
-			rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
63
+			rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, targetString, client.t("Cannot send to channel"))
40 64
 			return
41 65
 		}
42 66
 
43 67
 		if !channel.flags.HasMode(modes.ChanRoleplaying) {
44
-			rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, channel.name, client.t("Channel doesn't have roleplaying mode available"))
68
+			rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Channel doesn't have roleplaying mode available"))
69
+			return
70
+		}
71
+
72
+		if config.Roleplay.RequireChanops && !channel.ClientIsAtLeast(client, modes.ChannelOperator) {
73
+			rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, targetString, client.t("Insufficient privileges"))
45 74
 			return
46 75
 		}
47 76
 
48 77
 		for _, member := range channel.Members() {
49 78
 			for _, session := range member.Sessions() {
50
-				if member == client && !session.capabilities.Has(caps.EchoMessage) {
51
-					continue
52
-				} else if rb.session == session {
53
-					rb.Add(nil, source, "PRIVMSG", channel.name, message)
54
-				} else if member == client || session.capabilities.Has(caps.EchoMessage) {
55
-					session.Send(nil, source, "PRIVMSG", channel.name, message)
79
+				// see discussion on #865: clients do not understand how to do local echo
80
+				// of roleplay commands, so send them a copy whether they have echo-message
81
+				// or not
82
+				if rb.session == session {
83
+					rb.AddSplitMessageFromClient(source, "", nil, "PRIVMSG", targetString, splitMessage)
84
+				} else {
85
+					session.sendSplitMsgFromClientInternal(false, source, "", nil, "PRIVMSG", targetString, splitMessage)
56 86
 				}
57 87
 			}
58 88
 		}
89
+
90
+		channel.AddHistoryItem(history.Item{
91
+			Type:    history.Privmsg,
92
+			Message: splitMessage,
93
+			Nick:    source,
94
+		})
59 95
 	} else {
60 96
 		target, err := CasefoldName(targetString)
61 97
 		user := server.clients.Get(target)
@@ -71,9 +107,8 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
71 107
 
72 108
 		cnick := client.Nick()
73 109
 		tnick := user.Nick()
74
-		user.Send(nil, source, "PRIVMSG", tnick, message)
75
-		if rb.session.capabilities.Has(caps.EchoMessage) {
76
-			rb.Add(nil, source, "PRIVMSG", tnick, message)
110
+		for _, session := range user.Sessions() {
111
+			session.sendSplitMsgFromClientInternal(false, source, "", nil, "PRIVMSG", tnick, splitMessage)
77 112
 		}
78 113
 		if user.Away() {
79 114
 			//TODO(dan): possibly implement cooldown of away notifications to users

+ 7
- 0
irc/utils/args.go View File

@@ -82,3 +82,10 @@ func (err *IncompatibleSchemaError) Error() string {
82 82
 func NanoToTimestamp(nanotime int64) string {
83 83
 	return time.Unix(0, nanotime).UTC().Format(IRCv3TimestampFormat)
84 84
 }
85
+
86
+func BoolDefaultTrue(value *bool) bool {
87
+	if value != nil {
88
+		return *value
89
+	}
90
+	return true
91
+}

+ 18
- 0
oragono.yaml View File

@@ -492,6 +492,7 @@ oper-classes:
492 492
             - "local_ban"
493 493
             - "local_unban"
494 494
             - "nofakelag"
495
+            - "roleplay"
495 496
 
496 497
     # network operator
497 498
     "network-oper":
@@ -702,6 +703,23 @@ fakelag:
702 703
     # sending any commands:
703 704
     cooldown: 2s
704 705
 
706
+# the roleplay commands are semi-standardized extensions to IRC that allow
707
+# sending and receiving messages from pseudo-nicknames. this can be used either
708
+# for actual roleplaying, or for bridging IRC with other protocols.
709
+roleplay:
710
+    # are roleplay commands enabled at all? (channels and clients still have to
711
+    # opt in individually with the +E mode)
712
+    enabled: true
713
+
714
+    # require the "roleplay" oper capability to send roleplay messages?
715
+    require-oper: false
716
+
717
+    # require channel operator permissions to send roleplay messages?
718
+    require-chanops: false
719
+
720
+    # add the real nickname, in parentheses, to the end of every roleplay message?
721
+    add-suffix: true
722
+
705 723
 # message history tracking, for the RESUME extension and possibly other uses in future
706 724
 history:
707 725
     # should we store messages for later playback?

Loading…
Cancel
Save