mirror of
https://github.com/ergochat/ergo.git
synced 2025-02-27 11:00:43 +01:00
implement channel modes and messages properly
This commit is contained in:
parent
0bfa2fb98f
commit
488b2ccf8f
@ -84,6 +84,14 @@ func (channel *Channel) receiveCommands(commands <-chan ChannelCommand) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsPrivMsg(reply Reply) bool {
|
||||||
|
strReply, ok := reply.(*StringReply)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return strReply.code == "PRIVMSG"
|
||||||
|
}
|
||||||
|
|
||||||
func (channel *Channel) receiveReplies(replies <-chan Reply) {
|
func (channel *Channel) receiveReplies(replies <-chan Reply) {
|
||||||
for reply := range replies {
|
for reply := range replies {
|
||||||
if channel.destroyed {
|
if channel.destroyed {
|
||||||
@ -98,9 +106,10 @@ func (channel *Channel) receiveReplies(replies <-chan Reply) {
|
|||||||
}
|
}
|
||||||
channel.mutex.Lock()
|
channel.mutex.Lock()
|
||||||
for client := range channel.members {
|
for client := range channel.members {
|
||||||
if reply.Source() != Identifier(client) {
|
if IsPrivMsg(reply) && (reply.Source() == Identifier(client)) {
|
||||||
client.Reply(reply)
|
continue
|
||||||
}
|
}
|
||||||
|
client.Reply(reply)
|
||||||
}
|
}
|
||||||
channel.mutex.Unlock()
|
channel.mutex.Unlock()
|
||||||
}
|
}
|
||||||
@ -187,9 +196,7 @@ func (channel *Channel) Join(client *Client) {
|
|||||||
channel.mutex.Unlock()
|
channel.mutex.Unlock()
|
||||||
|
|
||||||
client.channels.Add(channel)
|
client.channels.Add(channel)
|
||||||
reply := RplJoin(client, channel)
|
channel.Reply(RplJoin(client, channel))
|
||||||
client.Reply(reply)
|
|
||||||
channel.Reply(reply)
|
|
||||||
channel.GetTopic(client)
|
channel.GetTopic(client)
|
||||||
channel.GetUsers(client)
|
channel.GetUsers(client)
|
||||||
}
|
}
|
||||||
@ -216,9 +223,7 @@ func (m *PartCommand) HandleChannel(channel *Channel) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reply := RplPart(client, channel, m.Message())
|
channel.Reply(RplPart(client, channel, m.Message()))
|
||||||
client.Reply(reply)
|
|
||||||
channel.Reply(reply)
|
|
||||||
|
|
||||||
channel.members.Remove(client)
|
channel.members.Remove(client)
|
||||||
client.channels.Remove(channel)
|
client.channels.Remove(channel)
|
||||||
@ -245,9 +250,7 @@ func (m *TopicCommand) HandleChannel(channel *Channel) {
|
|||||||
|
|
||||||
channel.topic = m.topic
|
channel.topic = m.topic
|
||||||
channel.GetTopic(client)
|
channel.GetTopic(client)
|
||||||
reply := RplTopicMsg(client, channel)
|
channel.Reply(RplTopicMsg(client, channel))
|
||||||
client.Reply(reply)
|
|
||||||
channel.Reply(reply)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,10 +270,18 @@ func (m *PrivMsgCommand) HandleChannel(channel *Channel) {
|
|||||||
func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
|
func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
|
||||||
client := msg.Client()
|
client := msg.Client()
|
||||||
|
|
||||||
for _, modeOp := range msg.modeOps {
|
if len(msg.changes) == 0 {
|
||||||
switch modeOp.mode {
|
client.Reply(RplChannelModeIs(channel))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make(ChannelModeChanges, 0)
|
||||||
|
|
||||||
|
for _, change := range msg.changes {
|
||||||
|
switch change.mode {
|
||||||
case BanMask:
|
case BanMask:
|
||||||
// TODO add/remove
|
// TODO add/remove
|
||||||
|
|
||||||
for _, banMask := range channel.banList {
|
for _, banMask := range channel.banList {
|
||||||
client.Reply(RplBanList(channel, banMask))
|
client.Reply(RplBanList(channel, banMask))
|
||||||
}
|
}
|
||||||
@ -282,12 +293,14 @@ func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
switch modeOp.op {
|
switch change.op {
|
||||||
case Add:
|
case Add:
|
||||||
channel.flags[modeOp.mode] = true
|
channel.flags[change.mode] = true
|
||||||
|
changes = append(changes, change)
|
||||||
|
|
||||||
case Remove:
|
case Remove:
|
||||||
delete(channel.flags, modeOp.mode)
|
delete(channel.flags, change.mode)
|
||||||
|
changes = append(changes, change)
|
||||||
}
|
}
|
||||||
|
|
||||||
case Key:
|
case Key:
|
||||||
@ -296,34 +309,33 @@ func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
switch modeOp.op {
|
switch change.op {
|
||||||
case Add:
|
case Add:
|
||||||
if modeOp.arg == "" {
|
if change.arg == "" {
|
||||||
// TODO err reply
|
// TODO err reply
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.key = modeOp.arg
|
channel.key = change.arg
|
||||||
|
changes = append(changes, change)
|
||||||
|
|
||||||
case Remove:
|
case Remove:
|
||||||
channel.key = ""
|
channel.key = ""
|
||||||
}
|
changes = append(changes, change)
|
||||||
}
|
}
|
||||||
|
|
||||||
mmode := ChannelMemberMode(modeOp.mode)
|
|
||||||
switch mmode {
|
|
||||||
case ChannelOperator, Voice:
|
case ChannelOperator, Voice:
|
||||||
if !channel.ClientIsOperator(client) {
|
if !channel.ClientIsOperator(client) {
|
||||||
client.Reply(ErrChanOPrivIsNeeded(channel))
|
client.Reply(ErrChanOPrivIsNeeded(channel))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if modeOp.arg == "" {
|
if change.arg == "" {
|
||||||
// TODO err reply
|
// TODO err reply
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
target := channel.server.clients[modeOp.arg]
|
target := channel.server.clients[change.arg]
|
||||||
if target == nil {
|
if target == nil {
|
||||||
// TODO err reply
|
// TODO err reply
|
||||||
continue
|
continue
|
||||||
@ -334,16 +346,21 @@ func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
switch modeOp.op {
|
switch change.op {
|
||||||
case Add:
|
case Add:
|
||||||
channel.members[target][mmode] = true
|
channel.members[target][change.mode] = true
|
||||||
|
changes = append(changes, change)
|
||||||
|
|
||||||
case Remove:
|
case Remove:
|
||||||
channel.members[target][mmode] = false
|
channel.members[target][change.mode] = false
|
||||||
|
changes = append(changes, change)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Reply(RplChannelModeIs(channel))
|
if len(changes) > 0 {
|
||||||
|
channel.Reply(RplChannelMode(client, channel, changes))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *NoticeCommand) HandleChannel(channel *Channel) {
|
func (m *NoticeCommand) HandleChannel(channel *Channel) {
|
||||||
|
@ -402,6 +402,26 @@ func (change *ModeChange) String() string {
|
|||||||
return fmt.Sprintf("%s%s", change.op, change.mode)
|
return fmt.Sprintf("%s%s", change.op, change.mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ModeChanges []ModeChange
|
||||||
|
|
||||||
|
func (changes ModeChanges) String() string {
|
||||||
|
if len(changes) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
op := changes[0].op
|
||||||
|
str := changes[0].op.String()
|
||||||
|
for _, change := range changes {
|
||||||
|
if change.op == op {
|
||||||
|
str += change.mode.String()
|
||||||
|
} else {
|
||||||
|
op = change.op
|
||||||
|
str += " " + change.op.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
type ModeCommand struct {
|
type ModeCommand struct {
|
||||||
BaseCommand
|
BaseCommand
|
||||||
nickname string
|
nickname string
|
||||||
@ -436,27 +456,47 @@ func (cmd *ModeCommand) String() string {
|
|||||||
return fmt.Sprintf("MODE(nickname=%s, changes=%s)", cmd.nickname, cmd.changes)
|
return fmt.Sprintf("MODE(nickname=%s, changes=%s)", cmd.nickname, cmd.changes)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelModeOp struct {
|
type ChannelModeChange struct {
|
||||||
mode ChannelMode
|
mode ChannelMode
|
||||||
op ModeOp
|
op ModeOp
|
||||||
arg string
|
arg string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (op *ChannelModeOp) String() string {
|
func (op *ChannelModeChange) String() string {
|
||||||
return fmt.Sprintf("{%s %s %s}", op.op, op.mode, op.arg)
|
return fmt.Sprintf("{%s %s %s}", op.op, op.mode, op.arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChannelModeChanges []ChannelModeChange
|
||||||
|
|
||||||
|
func (changes ChannelModeChanges) String() string {
|
||||||
|
if len(changes) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
str := "+"
|
||||||
|
if changes[0].op == Remove {
|
||||||
|
str = "-"
|
||||||
|
}
|
||||||
|
for _, change := range changes {
|
||||||
|
str += change.mode.String()
|
||||||
|
}
|
||||||
|
for _, change := range changes {
|
||||||
|
str += " " + change.arg
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
type ChannelModeCommand struct {
|
type ChannelModeCommand struct {
|
||||||
BaseCommand
|
BaseCommand
|
||||||
channel string
|
channel string
|
||||||
modeOps []ChannelModeOp
|
changes ChannelModeChanges
|
||||||
}
|
}
|
||||||
|
|
||||||
// MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
|
// MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
|
||||||
func NewChannelModeCommand(args []string) (editableCommand, error) {
|
func NewChannelModeCommand(args []string) (editableCommand, error) {
|
||||||
cmd := &ChannelModeCommand{
|
cmd := &ChannelModeCommand{
|
||||||
channel: args[0],
|
channel: args[0],
|
||||||
modeOps: make([]ChannelModeOp, 0),
|
changes: make(ChannelModeChanges, 0),
|
||||||
}
|
}
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
|
|
||||||
@ -472,18 +512,18 @@ func NewChannelModeCommand(args []string) (editableCommand, error) {
|
|||||||
|
|
||||||
skipArgs := 1
|
skipArgs := 1
|
||||||
for _, mode := range modeArg {
|
for _, mode := range modeArg {
|
||||||
modeOp := ChannelModeOp{
|
change := ChannelModeChange{
|
||||||
mode: ChannelMode(mode),
|
mode: ChannelMode(mode),
|
||||||
op: op,
|
op: op,
|
||||||
}
|
}
|
||||||
switch modeOp.mode {
|
switch change.mode {
|
||||||
case Key, BanMask, ExceptionMask, InviteMask, UserLimit:
|
case Key, BanMask, ExceptionMask, InviteMask, UserLimit:
|
||||||
if len(args) > skipArgs {
|
if len(args) > skipArgs {
|
||||||
modeOp.arg = args[skipArgs]
|
change.arg = args[skipArgs]
|
||||||
skipArgs += 1
|
skipArgs += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmd.modeOps = append(cmd.modeOps, modeOp)
|
cmd.changes = append(cmd.changes, change)
|
||||||
}
|
}
|
||||||
args = args[skipArgs:]
|
args = args[skipArgs:]
|
||||||
}
|
}
|
||||||
@ -492,7 +532,7 @@ func NewChannelModeCommand(args []string) (editableCommand, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (msg *ChannelModeCommand) String() string {
|
func (msg *ChannelModeCommand) String() string {
|
||||||
return fmt.Sprintf("MODE(channel=%s, modeOps=%s)", msg.channel, msg.modeOps)
|
return fmt.Sprintf("MODE(channel=%s, changes=%s)", msg.channel, msg.changes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModeCommand(args []string) (editableCommand, error) {
|
func NewModeCommand(args []string) (editableCommand, error) {
|
||||||
|
@ -175,6 +175,8 @@ const (
|
|||||||
|
|
||||||
Anonymous ChannelMode = 'a' // flag
|
Anonymous ChannelMode = 'a' // flag
|
||||||
BanMask ChannelMode = 'b' // arg
|
BanMask ChannelMode = 'b' // arg
|
||||||
|
ChannelCreator ChannelMode = 'O' // flag
|
||||||
|
ChannelOperator ChannelMode = 'o' // arg
|
||||||
ExceptionMask ChannelMode = 'e' // arg
|
ExceptionMask ChannelMode = 'e' // arg
|
||||||
InviteMask ChannelMode = 'I' // arg
|
InviteMask ChannelMode = 'I' // arg
|
||||||
InviteOnly ChannelMode = 'i' // flag
|
InviteOnly ChannelMode = 'i' // flag
|
||||||
@ -187,10 +189,7 @@ const (
|
|||||||
ReOp ChannelMode = 'r' // flag
|
ReOp ChannelMode = 'r' // flag
|
||||||
Secret ChannelMode = 's' // flag
|
Secret ChannelMode = 's' // flag
|
||||||
UserLimit ChannelMode = 'l' // flag arg
|
UserLimit ChannelMode = 'l' // flag arg
|
||||||
|
Voice ChannelMode = 'v' // arg
|
||||||
ChannelCreator ChannelMemberMode = 'O' // flag
|
|
||||||
ChannelOperator ChannelMemberMode = 'o' // arg
|
|
||||||
Voice ChannelMemberMode = 'v' // arg
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
19
irc/reply.go
19
irc/reply.go
@ -144,11 +144,20 @@ func RplJoin(client *Client, channel *Channel) Reply {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RplPart(client *Client, channel *Channel, message string) Reply {
|
func RplPart(client *Client, channel *Channel, message string) Reply {
|
||||||
return NewStringReply(client, "PART", "%s :%s", channel.name, message)
|
return NewStringReply(client, "PART", "%s :%s", channel, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RplMode(client *Client, changes ModeChanges) Reply {
|
||||||
|
return NewStringReply(client, "MODE", "%s :%s", client.Nick(), changes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RplChannelMode(client *Client, channel *Channel,
|
||||||
|
changes ChannelModeChanges) Reply {
|
||||||
|
return NewStringReply(client, "MODE", "%s %s", channel, changes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplTopicMsg(source Identifier, channel *Channel) Reply {
|
func RplTopicMsg(source Identifier, channel *Channel) Reply {
|
||||||
return NewStringReply(source, "TOPIC", "%s :%s", channel.name, channel.topic)
|
return NewStringReply(source, "TOPIC", "%s :%s", channel, channel.topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplPing(server *Server, target Identifier) Reply {
|
func RplPing(server *Server, target Identifier) Reply {
|
||||||
@ -207,9 +216,11 @@ func RplTopic(channel *Channel) Reply {
|
|||||||
"%s :%s", channel.name, channel.topic)
|
"%s :%s", channel.name, channel.topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// <nick> <channel>
|
||||||
|
// NB: correction in errata
|
||||||
func RplInvitingMsg(channel *Channel, invitee *Client) Reply {
|
func RplInvitingMsg(channel *Channel, invitee *Client) Reply {
|
||||||
return NewNumericReply(channel.server, RPL_INVITING,
|
return NewNumericReply(channel.server, RPL_INVITING,
|
||||||
"%s %s", channel.name, invitee.Nick())
|
"%s %s", invitee.Nick(), channel.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplNamReply(channel *Channel, names []string) *NumericReply {
|
func RplNamReply(channel *Channel, names []string) *NumericReply {
|
||||||
@ -238,7 +249,7 @@ func RplEndOfWhois(server *Server) Reply {
|
|||||||
|
|
||||||
func RplChannelModeIs(channel *Channel) Reply {
|
func RplChannelModeIs(channel *Channel) Reply {
|
||||||
return NewNumericReply(channel.server, RPL_CHANNELMODEIS, "%s %s",
|
return NewNumericReply(channel.server, RPL_CHANNELMODEIS, "%s %s",
|
||||||
channel.name, channel.ModeString())
|
channel, channel.ModeString())
|
||||||
}
|
}
|
||||||
|
|
||||||
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
|
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
|
||||||
|
@ -270,7 +270,6 @@ func (m *NickCommand) HandleRegServer(s *Server) {
|
|||||||
|
|
||||||
client.nick = m.nickname
|
client.nick = m.nickname
|
||||||
s.clients.Add(client)
|
s.clients.Add(client)
|
||||||
client.Reply(RplNick(client, m.nickname))
|
|
||||||
s.tryRegister(client)
|
s.tryRegister(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,22 +413,31 @@ func (m *PrivMsgCommand) HandleServer(s *Server) {
|
|||||||
func (m *ModeCommand) HandleServer(s *Server) {
|
func (m *ModeCommand) HandleServer(s *Server) {
|
||||||
client := m.Client()
|
client := m.Client()
|
||||||
target := s.clients[m.nickname]
|
target := s.clients[m.nickname]
|
||||||
if client == target {
|
// TODO other auth
|
||||||
|
if client != target {
|
||||||
|
client.Reply(ErrUsersDontMatch(s))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := make(ModeChanges, 0)
|
||||||
|
|
||||||
for _, change := range m.changes {
|
for _, change := range m.changes {
|
||||||
if change.mode == Invisible {
|
if change.mode == Invisible {
|
||||||
switch change.op {
|
switch change.op {
|
||||||
case Add:
|
case Add:
|
||||||
client.invisible = true
|
client.invisible = true
|
||||||
|
changes = append(changes, change)
|
||||||
|
|
||||||
case Remove:
|
case Remove:
|
||||||
client.invisible = false
|
client.invisible = false
|
||||||
|
changes = append(changes, change)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client.Reply(RplUModeIs(s, client))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client.Reply(ErrUsersDontMatch(s))
|
if len(changes) > 0 {
|
||||||
|
client.Reply(RplMode(client, changes))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *WhoisCommand) HandleServer(server *Server) {
|
func (m *WhoisCommand) HandleServer(server *Server) {
|
||||||
|
15
irc/types.go
15
irc/types.go
@ -15,6 +15,10 @@ type Mask string
|
|||||||
// add, remove, list modes
|
// add, remove, list modes
|
||||||
type ModeOp rune
|
type ModeOp rune
|
||||||
|
|
||||||
|
func (op ModeOp) String() string {
|
||||||
|
return string(op)
|
||||||
|
}
|
||||||
|
|
||||||
// user mode flags
|
// user mode flags
|
||||||
type UserMode rune
|
type UserMode rune
|
||||||
|
|
||||||
@ -37,9 +41,6 @@ func (mode ChannelMode) String() string {
|
|||||||
return fmt.Sprintf("%c", mode)
|
return fmt.Sprintf("%c", mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// user-channel mode flags
|
|
||||||
type ChannelMemberMode rune
|
|
||||||
|
|
||||||
type ChannelNameMap map[string]*Channel
|
type ChannelNameMap map[string]*Channel
|
||||||
|
|
||||||
func (channels ChannelNameMap) Add(channel *Channel) error {
|
func (channels ChannelNameMap) Add(channel *Channel) error {
|
||||||
@ -84,19 +85,19 @@ func (clients ClientNameMap) Remove(client *Client) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelMemberModeSet map[ChannelMemberMode]bool
|
type ChannelModeSet map[ChannelMode]bool
|
||||||
|
|
||||||
type ClientSet map[*Client]ChannelMemberModeSet
|
type ClientSet map[*Client]ChannelModeSet
|
||||||
|
|
||||||
func (clients ClientSet) Add(client *Client) {
|
func (clients ClientSet) Add(client *Client) {
|
||||||
clients[client] = make(ChannelMemberModeSet)
|
clients[client] = make(ChannelModeSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clients ClientSet) Remove(client *Client) {
|
func (clients ClientSet) Remove(client *Client) {
|
||||||
delete(clients, client)
|
delete(clients, client)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (clients ClientSet) HasMode(client *Client, mode ChannelMemberMode) bool {
|
func (clients ClientSet) HasMode(client *Client, mode ChannelMode) bool {
|
||||||
modes, ok := clients[client]
|
modes, ok := clients[client]
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
|
Loading…
x
Reference in New Issue
Block a user