mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-10 22:19:31 +01:00
roleplay: Initial commit
This commit is contained in:
parent
62a0cbc1f6
commit
07e4728c15
@ -15,6 +15,7 @@ New release of Oragono!
|
||||
* Length of channel mode lists (ban / ban-except / invite-except) is now restricted to the limit in config.
|
||||
* Support `MAXLIST`, `MAXTARGETS`, `MODES`, `TARGMAX` in `RPL_ISUPPORT`.
|
||||
* Added support for IRCv3 capability [`chghost`](http://ircv3.net/specs/extensions/chghost-3.2.html).
|
||||
* Roleplaying commands, both inside channels and between clients.
|
||||
|
||||
### Changed
|
||||
* In the config file, "operator" changed to "opers", and new oper class is required.
|
||||
|
@ -461,6 +461,8 @@ func (client *Client) Send(tags *map[string]ircmsg.TagValue, prefix string, comm
|
||||
line, err := message.Line()
|
||||
if err != nil {
|
||||
// try not to fail quietly - especially useful when running tests, as a note to dig deeper
|
||||
// log.Println("Error assembling message:")
|
||||
// spew.Dump(message)
|
||||
message = ircmsg.MakeMessage(nil, client.server.name, ERR_UNKNOWNERROR, "*", "Error assembling message for sending")
|
||||
line, _ := message.Line()
|
||||
client.socket.Write(line)
|
||||
|
@ -54,6 +54,10 @@ func (cmd *Command) Run(server *Server, client *Client, msg ircmsg.IrcMessage) b
|
||||
|
||||
// Commands holds all commands executable by a client connected to us.
|
||||
var Commands = map[string]Command{
|
||||
"AMBIANCE": {
|
||||
handler: sceneHandler,
|
||||
minParams: 2,
|
||||
},
|
||||
"AUTHENTICATE": {
|
||||
handler: authenticateHandler,
|
||||
usablePreReg: true,
|
||||
@ -127,6 +131,14 @@ var Commands = map[string]Command{
|
||||
handler: noticeHandler,
|
||||
minParams: 2,
|
||||
},
|
||||
"NPC": {
|
||||
handler: npcHandler,
|
||||
minParams: 3,
|
||||
},
|
||||
"NPCA": {
|
||||
handler: npcaHandler,
|
||||
minParams: 3,
|
||||
},
|
||||
"OPER": {
|
||||
handler: operHandler,
|
||||
minParams: 2,
|
||||
@ -161,6 +173,10 @@ var Commands = map[string]Command{
|
||||
minParams: 2,
|
||||
oper: true,
|
||||
},
|
||||
"SCENE": {
|
||||
handler: sceneHandler,
|
||||
minParams: 2,
|
||||
},
|
||||
"QUIT": {
|
||||
handler: quitHandler,
|
||||
usablePreReg: true,
|
||||
|
20
irc/help.go
20
irc/help.go
@ -60,6 +60,11 @@ Oragono supports the following user modes:
|
||||
// Help contains the help strings distributed with the IRCd.
|
||||
var Help = map[string]HelpEntry{
|
||||
// Commands
|
||||
"ambiance": {
|
||||
text: `AMBIANCE <target> <text to be sent>
|
||||
|
||||
The AMBIANCE command is used to send a scene notification to the given target.`,
|
||||
},
|
||||
"authenticate": {
|
||||
text: `AUTHENTICATE
|
||||
|
||||
@ -182,6 +187,16 @@ Sets your nickname to the new given one.`,
|
||||
text: `NOTICE <target>{,<target>} <text to be sent>
|
||||
|
||||
Sends the text to the given targets as a NOTICE.`,
|
||||
},
|
||||
"npc": {
|
||||
text: `NPC <target> <sourcenick> <text to be sent>
|
||||
|
||||
The NPC command is used to send a message to the target as the source.`,
|
||||
},
|
||||
"npca": {
|
||||
text: `NPCA <target> <sourcenick> <text to be sent>
|
||||
|
||||
The NPC command is used to send an action to the target as the source.`,
|
||||
},
|
||||
"oper": {
|
||||
text: `OPER <name> <password>
|
||||
@ -219,6 +234,11 @@ Sends the text to the given targets as a PRIVMSG.`,
|
||||
text: `SANICK <currentnick> <newnick>
|
||||
|
||||
Gives the given user a new nickname.`,
|
||||
},
|
||||
"scene": {
|
||||
text: `SCENE <target> <text to be sent>
|
||||
|
||||
The SCENE command is used to send a scene notification to the given target.`,
|
||||
},
|
||||
"quit": {
|
||||
text: `QUIT [reason]
|
||||
|
10
irc/modes.go
10
irc/modes.go
@ -144,12 +144,13 @@ const (
|
||||
Restricted UserMode = 'r'
|
||||
ServerNotice UserMode = 's' // deprecated
|
||||
TLS UserMode = 'Z'
|
||||
UserRoleplaying UserMode = 'E'
|
||||
WallOps UserMode = 'w'
|
||||
)
|
||||
|
||||
var (
|
||||
SupportedUserModes = UserModes{
|
||||
Away, Invisible, Operator,
|
||||
Away, Invisible, Operator, UserRoleplaying,
|
||||
}
|
||||
// supportedUserModesString acts as a cache for when we introduce users
|
||||
supportedUserModesString = SupportedUserModes.String()
|
||||
@ -157,6 +158,7 @@ var (
|
||||
|
||||
const (
|
||||
BanMask ChannelMode = 'b' // arg
|
||||
ChanRoleplaying ChannelMode = 'E' // flag
|
||||
ExceptMask ChannelMode = 'e' // arg
|
||||
InviteMask ChannelMode = 'I' // arg
|
||||
InviteOnly ChannelMode = 'i' // flag
|
||||
@ -177,7 +179,7 @@ var (
|
||||
|
||||
SupportedChannelModes = ChannelModes{
|
||||
BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
|
||||
OpOnlyTopic, Secret, UserLimit,
|
||||
OpOnlyTopic, Secret, UserLimit, ChanRoleplaying,
|
||||
}
|
||||
// supportedChannelModesString acts as a cache for when we introduce users
|
||||
supportedChannelModesString = SupportedChannelModes.String()
|
||||
@ -297,7 +299,7 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||
|
||||
for _, change := range changes {
|
||||
switch change.mode {
|
||||
case Invisible, ServerNotice, WallOps:
|
||||
case Invisible, ServerNotice, WallOps, UserRoleplaying:
|
||||
switch change.op {
|
||||
case Add:
|
||||
if target.flags[change.mode] {
|
||||
@ -471,7 +473,7 @@ func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||
}
|
||||
applied = append(applied, change)
|
||||
|
||||
case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Secret:
|
||||
case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Secret, ChanRoleplaying:
|
||||
switch change.op {
|
||||
case Add:
|
||||
if channel.flags[change.mode] {
|
||||
|
@ -25,7 +25,7 @@ func nickHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if err != nil || len(nicknameRaw) > server.limits.NickLen {
|
||||
if err != nil || len(nicknameRaw) > server.limits.NickLen || nickname == "=scene=" {
|
||||
client.Send(nil, server.name, ERR_ERRONEUSNICKNAME, client.nick, nicknameRaw, "Erroneous nickname")
|
||||
return false
|
||||
}
|
||||
@ -59,14 +59,14 @@ func sanickHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||
}
|
||||
|
||||
oldnick, oerr := CasefoldName(msg.Params[0])
|
||||
casefoldedNickname, err := CasefoldName(msg.Params[1])
|
||||
nickname, err := CasefoldName(msg.Params[1])
|
||||
|
||||
if len(casefoldedNickname) < 1 {
|
||||
if len(nickname) < 1 {
|
||||
client.Send(nil, server.name, ERR_NONICKNAMEGIVEN, client.nick, "No nickname given")
|
||||
return false
|
||||
}
|
||||
|
||||
if oerr != nil || err != nil || len(strings.TrimSpace(msg.Params[1])) > server.limits.NickLen {
|
||||
if oerr != nil || err != nil || len(strings.TrimSpace(msg.Params[1])) > server.limits.NickLen || nickname == "=scene=" {
|
||||
client.Send(nil, server.name, ERR_ERRONEUSNICKNAME, client.nick, msg.Params[0], "Erroneous nickname")
|
||||
return false
|
||||
}
|
||||
@ -82,7 +82,7 @@ func sanickHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||
}
|
||||
|
||||
//TODO(dan): There's probably some races here, we should be changing this in the primary server thread
|
||||
if server.clients.Get(casefoldedNickname) != nil || server.clients.Get(casefoldedNickname) != target {
|
||||
if server.clients.Get(nickname) != nil || server.clients.Get(nickname) != target {
|
||||
client.Send(nil, server.name, ERR_NICKNAMEINUSE, client.nick, msg.Params[0], "Nickname is already in use")
|
||||
return false
|
||||
}
|
||||
|
94
irc/roleplay.go
Normal file
94
irc/roleplay.go
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
|
||||
// released under the MIT license
|
||||
|
||||
package irc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/DanielOaks/girc-go/ircmsg"
|
||||
)
|
||||
|
||||
const (
|
||||
npcNickMask = "%s!%s@npc.fakeuser.invalid"
|
||||
sceneNickMask = "=Scene=!%s@npc.fakeuser.invalid"
|
||||
)
|
||||
|
||||
// SCENE <target> <text to be sent>
|
||||
func sceneHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||
target := msg.Params[0]
|
||||
message := msg.Params[1]
|
||||
sourceString := fmt.Sprintf(sceneNickMask, client.nick)
|
||||
|
||||
sendRoleplayMessage(server, client, sourceString, target, false, message)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// NPC <target> <text to be sent>
|
||||
func npcHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||
target := msg.Params[0]
|
||||
fakeSource := msg.Params[1]
|
||||
message := msg.Params[2]
|
||||
sourceString := fmt.Sprintf(npcNickMask, fakeSource, client.nick)
|
||||
|
||||
sendRoleplayMessage(server, client, sourceString, target, false, message)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// NPCA <target> <text to be sent>
|
||||
func npcaHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||
target := msg.Params[0]
|
||||
fakeSource := msg.Params[1]
|
||||
message := msg.Params[2]
|
||||
sourceString := fmt.Sprintf(npcNickMask, fakeSource, client.nick)
|
||||
|
||||
sendRoleplayMessage(server, client, sourceString, target, true, message)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func sendRoleplayMessage(server *Server, client *Client, source string, targetString string, isAction bool, message string) {
|
||||
if isAction {
|
||||
message = fmt.Sprintf("\x01ACTION %s (%s)\x01", message, client.nick)
|
||||
} else {
|
||||
message = fmt.Sprintf("%s (%s)", message, client.nick)
|
||||
}
|
||||
|
||||
target, cerr := CasefoldChannel(targetString)
|
||||
if cerr == nil {
|
||||
channel := server.channels.Get(target)
|
||||
if channel == nil {
|
||||
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, targetString, "No such channel")
|
||||
return
|
||||
}
|
||||
|
||||
if !channel.CanSpeak(client) {
|
||||
client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, "Cannot send to channel")
|
||||
return
|
||||
}
|
||||
|
||||
for member := range channel.members {
|
||||
if member == client && !client.capabilities[EchoMessage] {
|
||||
continue
|
||||
}
|
||||
member.Send(nil, source, "PRIVMSG", channel.name, message)
|
||||
}
|
||||
} else {
|
||||
target, err := CasefoldName(targetString)
|
||||
user := server.clients.Get(target)
|
||||
if err != nil || user == nil {
|
||||
client.Send(nil, server.name, ERR_NOSUCHNICK, target, "No such nick")
|
||||
return
|
||||
}
|
||||
user.Send(nil, source, "PRIVMSG", user.nick, message)
|
||||
if client.capabilities[EchoMessage] {
|
||||
client.Send(nil, source, "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)
|
||||
}
|
||||
}
|
||||
}
|
@ -264,7 +264,7 @@ func (server *Server) setISupport() {
|
||||
server.isupport = NewISupportList()
|
||||
server.isupport.Add("AWAYLEN", strconv.Itoa(server.limits.AwayLen))
|
||||
server.isupport.Add("CASEMAPPING", "rfc7700")
|
||||
server.isupport.Add("CHANMODES", strings.Join([]string{ChannelModes{BanMask, ExceptMask, InviteMask}.String(), "", ChannelModes{UserLimit, Key}.String(), ChannelModes{InviteOnly, Moderated, NoOutside, OpOnlyTopic, Secret}.String()}, ","))
|
||||
server.isupport.Add("CHANMODES", strings.Join([]string{ChannelModes{BanMask, ExceptMask, InviteMask}.String(), "", ChannelModes{UserLimit, Key}.String(), ChannelModes{InviteOnly, Moderated, NoOutside, OpOnlyTopic, ChanRoleplaying, Secret}.String()}, ","))
|
||||
server.isupport.Add("CHANNELLEN", strconv.Itoa(server.limits.ChannelLen))
|
||||
server.isupport.Add("CHANTYPES", "#")
|
||||
server.isupport.Add("EXCEPTS", "")
|
||||
@ -277,6 +277,8 @@ func (server *Server) setISupport() {
|
||||
server.isupport.Add("NETWORK", server.networkName)
|
||||
server.isupport.Add("NICKLEN", strconv.Itoa(server.limits.NickLen))
|
||||
server.isupport.Add("PREFIX", "(qaohv)~&@%+")
|
||||
server.isupport.Add("RPCHAN", "E")
|
||||
server.isupport.Add("RPUSER", "E")
|
||||
server.isupport.Add("STATUSMSG", "~&@%+")
|
||||
server.isupport.Add("TARGMAX", fmt.Sprintf("NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:%s,NOTICE:%s,MONITOR:", maxTargetsString, maxTargetsString))
|
||||
server.isupport.Add("TOPICLEN", strconv.Itoa(server.limits.TopicLen))
|
||||
@ -1436,7 +1438,7 @@ func versionHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool
|
||||
target = msg.Params[0]
|
||||
}
|
||||
casefoldedTarget, err := Casefold(target)
|
||||
if (target != "") && err != nil || (casefoldedTarget != server.nameCasefolded) {
|
||||
if target != "" && (err != nil || casefoldedTarget != server.nameCasefolded) {
|
||||
client.Send(nil, server.name, ERR_NOSUCHSERVER, client.nick, target, "No such server")
|
||||
return false
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user