mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-22 11:59:40 +01:00
Support STATUSMSG
This commit is contained in:
parent
d9db688963
commit
517893065b
@ -12,10 +12,12 @@ New release of Oragono!
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
* Added `REHASH` command.
|
* Added `REHASH` command.
|
||||||
|
* Added ability to message channel members with a specific privelege (i.e. support for `STATUSMSG`).
|
||||||
* Added ability to enable and disable SASL.
|
* Added ability to enable and disable SASL.
|
||||||
* Added support for IRCv3 capabilities [`cap-notify`](http://ircv3.net/specs/extensions/cap-notify-3.2.html) and [`echo-message`](http://ircv3.net/specs/extensions/echo-message-3.2.html).
|
* Added support for IRCv3 capabilities [`cap-notify`](http://ircv3.net/specs/extensions/cap-notify-3.2.html) and [`echo-message`](http://ircv3.net/specs/extensions/echo-message-3.2.html).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
* Server operators no longer have permissions to do everything in channels.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
@ -91,14 +91,25 @@ func (channel *Channel) Names(client *Client) {
|
|||||||
client.Send(nil, client.server.name, RPL_ENDOFNAMES, client.nick, channel.name, "End of NAMES list")
|
client.Send(nil, client.server.name, RPL_ENDOFNAMES, client.nick, channel.name, "End of NAMES list")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientIsHalfOp returns whether client is at least a halfop.
|
// ClientIsAtLeast returns whether the client has at least the given channel privilege.
|
||||||
func (channel *Channel) ClientIsHalfOp(client *Client) bool {
|
func (channel *Channel) ClientIsAtLeast(client *Client, permission ChannelMode) bool {
|
||||||
return client.flags[Operator] || channel.members.HasMode(client, Halfop) || channel.members.HasMode(client, ChannelOperator) || channel.members.HasMode(client, ChannelAdmin) || channel.members.HasMode(client, ChannelFounder)
|
// get voice, since it's not a part of ChannelPrivModes
|
||||||
}
|
if channel.members.HasMode(client, permission) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ClientIsOperator returns whether client is at least a chanop.
|
// check regular modes
|
||||||
func (channel *Channel) ClientIsOperator(client *Client) bool {
|
for _, mode := range ChannelPrivModes {
|
||||||
return client.flags[Operator] || channel.members.HasMode(client, ChannelOperator) || channel.members.HasMode(client, ChannelAdmin) || channel.members.HasMode(client, ChannelFounder)
|
if channel.members.HasMode(client, mode) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if mode == permission {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefixes returns a list of prefixes for the given set of channel modes.
|
// Prefixes returns a list of prefixes for the given set of channel modes.
|
||||||
@ -276,7 +287,7 @@ func (channel *Channel) SetTopic(client *Client, topic string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if channel.flags[OpOnlyTopic] && !channel.ClientIsOperator(client) {
|
if channel.flags[OpOnlyTopic] && !channel.ClientIsAtLeast(client, ChannelOperator) {
|
||||||
client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -308,12 +319,22 @@ func (channel *Channel) CanSpeak(client *Client) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) PrivMsg(clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message string) {
|
// PrivMsg sends a private message to everyone in this channel.
|
||||||
|
func (channel *Channel) PrivMsg(minPrefix *ChannelMode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message string) {
|
||||||
if !channel.CanSpeak(client) {
|
if !channel.CanSpeak(client) {
|
||||||
client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, "Cannot send to channel")
|
client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, "Cannot send to channel")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// for STATUSMSG
|
||||||
|
var minPrefixMode ChannelMode
|
||||||
|
if minPrefix != nil {
|
||||||
|
minPrefixMode = *minPrefix
|
||||||
|
}
|
||||||
for member := range channel.members {
|
for member := range channel.members {
|
||||||
|
if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
|
||||||
|
// STATUSMSG
|
||||||
|
continue
|
||||||
|
}
|
||||||
if member == client && !client.capabilities[EchoMessage] {
|
if member == client && !client.capabilities[EchoMessage] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -327,7 +348,7 @@ func (channel *Channel) PrivMsg(clientOnlyTags *map[string]ircmsg.TagValue, clie
|
|||||||
|
|
||||||
func (channel *Channel) applyModeFlag(client *Client, mode ChannelMode,
|
func (channel *Channel) applyModeFlag(client *Client, mode ChannelMode,
|
||||||
op ModeOp) bool {
|
op ModeOp) bool {
|
||||||
if !channel.ClientIsOperator(client) {
|
if !channel.ClientIsAtLeast(client, ChannelOperator) {
|
||||||
client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -418,7 +439,7 @@ func (channel *Channel) applyModeMask(client *Client, mode ChannelMode, op ModeO
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !channel.ClientIsOperator(client) {
|
if !channel.ClientIsAtLeast(client, ChannelOperator) {
|
||||||
client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -461,7 +482,7 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string) {
|
|||||||
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
|
client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !channel.ClientIsOperator(client) {
|
if !channel.ClientIsAtLeast(client, ChannelOperator) {
|
||||||
client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, "Cannot send to channel")
|
client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, "Cannot send to channel")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -481,7 +502,7 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) Invite(invitee *Client, inviter *Client) {
|
func (channel *Channel) Invite(invitee *Client, inviter *Client) {
|
||||||
if channel.flags[InviteOnly] && !channel.ClientIsOperator(inviter) {
|
if channel.flags[InviteOnly] && !channel.ClientIsAtLeast(inviter, ChannelOperator) {
|
||||||
inviter.Send(nil, inviter.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
inviter.Send(nil, inviter.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -498,7 +519,7 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) {
|
|||||||
|
|
||||||
// send invite-notify
|
// send invite-notify
|
||||||
for member := range channel.members {
|
for member := range channel.members {
|
||||||
if member.capabilities[InviteNotify] && member != inviter && member != invitee && channel.ClientIsHalfOp(member) {
|
if member.capabilities[InviteNotify] && member != inviter && member != invitee && channel.ClientIsAtLeast(member, Halfop) {
|
||||||
member.Send(nil, inviter.nickMaskString, "INVITE", invitee.nick, channel.name)
|
member.Send(nil, inviter.nickMaskString, "INVITE", invitee.nick, channel.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
irc/modes.go
44
irc/modes.go
@ -156,12 +156,6 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ChannelFounder ChannelMode = 'q' // arg
|
|
||||||
ChannelAdmin ChannelMode = 'a' // arg
|
|
||||||
ChannelOperator ChannelMode = 'o' // arg
|
|
||||||
Halfop ChannelMode = 'h' // arg
|
|
||||||
Voice ChannelMode = 'v' // arg
|
|
||||||
|
|
||||||
BanMask ChannelMode = 'b' // arg
|
BanMask ChannelMode = 'b' // arg
|
||||||
ExceptMask ChannelMode = 'e' // arg
|
ExceptMask ChannelMode = 'e' // arg
|
||||||
InviteMask ChannelMode = 'I' // arg
|
InviteMask ChannelMode = 'I' // arg
|
||||||
@ -175,6 +169,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
ChannelFounder ChannelMode = 'q' // arg
|
||||||
|
ChannelAdmin ChannelMode = 'a' // arg
|
||||||
|
ChannelOperator ChannelMode = 'o' // arg
|
||||||
|
Halfop ChannelMode = 'h' // arg
|
||||||
|
Voice ChannelMode = 'v' // arg
|
||||||
|
|
||||||
SupportedChannelModes = ChannelModes{
|
SupportedChannelModes = ChannelModes{
|
||||||
BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
|
BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
|
||||||
OpOnlyTopic, Secret, UserLimit,
|
OpOnlyTopic, Secret, UserLimit,
|
||||||
@ -201,6 +201,38 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SplitChannelMembershipPrefixes takes a target and returns the prefixes on it, then the name.
|
||||||
|
func SplitChannelMembershipPrefixes(target string) (prefixes string, name string) {
|
||||||
|
name = target
|
||||||
|
for {
|
||||||
|
if len(name) == 0 || strings.Contains("~&@%+", string(name[0])) {
|
||||||
|
prefixes += string(name[0])
|
||||||
|
name = name[1:]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefixes, name
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLowestChannelModePrefix returns the lowest channel prefix mode out of the given prefixes.
|
||||||
|
func GetLowestChannelModePrefix(prefixes string) *ChannelMode {
|
||||||
|
var lowest *ChannelMode
|
||||||
|
|
||||||
|
if strings.Contains(prefixes, "+") {
|
||||||
|
lowest = &Voice
|
||||||
|
} else {
|
||||||
|
for i, mode := range ChannelPrivModes {
|
||||||
|
if strings.Contains(prefixes, ChannelModePrefixes[mode]) {
|
||||||
|
lowest = &ChannelPrivModes[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lowest
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// commands
|
// commands
|
||||||
//
|
//
|
||||||
|
@ -236,7 +236,7 @@ func (server *Server) setISupport() {
|
|||||||
server.isupport.Add("NETWORK", server.networkName)
|
server.isupport.Add("NETWORK", server.networkName)
|
||||||
server.isupport.Add("NICKLEN", strconv.Itoa(server.limits.NickLen))
|
server.isupport.Add("NICKLEN", strconv.Itoa(server.limits.NickLen))
|
||||||
server.isupport.Add("PREFIX", "(qaohv)~&@%+")
|
server.isupport.Add("PREFIX", "(qaohv)~&@%+")
|
||||||
// server.isupport.Add("STATUSMSG", "@+") //TODO(dan): Support STATUSMSG
|
server.isupport.Add("STATUSMSG", "~&@%+")
|
||||||
// server.isupport.Add("TARGMAX", "") //TODO(dan): Support this
|
// server.isupport.Add("TARGMAX", "") //TODO(dan): Support this
|
||||||
server.isupport.Add("TOPICLEN", strconv.Itoa(server.limits.TopicLen))
|
server.isupport.Add("TOPICLEN", strconv.Itoa(server.limits.TopicLen))
|
||||||
|
|
||||||
@ -686,6 +686,9 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool
|
|||||||
message := msg.Params[1]
|
message := msg.Params[1]
|
||||||
|
|
||||||
for _, targetString := range targets {
|
for _, targetString := range targets {
|
||||||
|
prefixes, targetString := SplitChannelMembershipPrefixes(targetString)
|
||||||
|
lowestPrefix := GetLowestChannelModePrefix(prefixes)
|
||||||
|
|
||||||
target, err := CasefoldChannel(targetString)
|
target, err := CasefoldChannel(targetString)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
channel := server.channels.Get(target)
|
channel := server.channels.Get(target)
|
||||||
@ -693,7 +696,7 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool
|
|||||||
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, targetString, "No such channel")
|
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, targetString, "No such channel")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
channel.PrivMsg(clientOnlyTags, client, message)
|
channel.PrivMsg(lowestPrefix, clientOnlyTags, client, message)
|
||||||
} else {
|
} else {
|
||||||
target, err = CasefoldName(targetString)
|
target, err = CasefoldName(targetString)
|
||||||
user := server.clients.Get(target)
|
user := server.clients.Get(target)
|
||||||
@ -1112,6 +1115,9 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
message := msg.Params[1]
|
message := msg.Params[1]
|
||||||
|
|
||||||
for _, targetString := range targets {
|
for _, targetString := range targets {
|
||||||
|
prefixes, targetString := SplitChannelMembershipPrefixes(targetString)
|
||||||
|
lowestPrefix := GetLowestChannelModePrefix(prefixes)
|
||||||
|
|
||||||
target, cerr := CasefoldChannel(targetString)
|
target, cerr := CasefoldChannel(targetString)
|
||||||
if cerr == nil {
|
if cerr == nil {
|
||||||
channel := server.channels.Get(target)
|
channel := server.channels.Get(target)
|
||||||
@ -1119,7 +1125,7 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
// errors silently ignored with NOTICE as per RFC
|
// errors silently ignored with NOTICE as per RFC
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
channel.PrivMsg(clientOnlyTags, client, message)
|
channel.PrivMsg(lowestPrefix, clientOnlyTags, client, message)
|
||||||
} else {
|
} else {
|
||||||
target, err := CasefoldName(targetString)
|
target, err := CasefoldName(targetString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user