mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-25 21:39:25 +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) {
|
||||
for reply := range replies {
|
||||
if channel.destroyed {
|
||||
@ -98,9 +106,10 @@ func (channel *Channel) receiveReplies(replies <-chan Reply) {
|
||||
}
|
||||
channel.mutex.Lock()
|
||||
for client := range channel.members {
|
||||
if reply.Source() != Identifier(client) {
|
||||
client.Reply(reply)
|
||||
if IsPrivMsg(reply) && (reply.Source() == Identifier(client)) {
|
||||
continue
|
||||
}
|
||||
client.Reply(reply)
|
||||
}
|
||||
channel.mutex.Unlock()
|
||||
}
|
||||
@ -187,9 +196,7 @@ func (channel *Channel) Join(client *Client) {
|
||||
channel.mutex.Unlock()
|
||||
|
||||
client.channels.Add(channel)
|
||||
reply := RplJoin(client, channel)
|
||||
client.Reply(reply)
|
||||
channel.Reply(reply)
|
||||
channel.Reply(RplJoin(client, channel))
|
||||
channel.GetTopic(client)
|
||||
channel.GetUsers(client)
|
||||
}
|
||||
@ -216,9 +223,7 @@ func (m *PartCommand) HandleChannel(channel *Channel) {
|
||||
return
|
||||
}
|
||||
|
||||
reply := RplPart(client, channel, m.Message())
|
||||
client.Reply(reply)
|
||||
channel.Reply(reply)
|
||||
channel.Reply(RplPart(client, channel, m.Message()))
|
||||
|
||||
channel.members.Remove(client)
|
||||
client.channels.Remove(channel)
|
||||
@ -245,9 +250,7 @@ func (m *TopicCommand) HandleChannel(channel *Channel) {
|
||||
|
||||
channel.topic = m.topic
|
||||
channel.GetTopic(client)
|
||||
reply := RplTopicMsg(client, channel)
|
||||
client.Reply(reply)
|
||||
channel.Reply(reply)
|
||||
channel.Reply(RplTopicMsg(client, channel))
|
||||
return
|
||||
}
|
||||
|
||||
@ -267,10 +270,18 @@ func (m *PrivMsgCommand) HandleChannel(channel *Channel) {
|
||||
func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
|
||||
client := msg.Client()
|
||||
|
||||
for _, modeOp := range msg.modeOps {
|
||||
switch modeOp.mode {
|
||||
if len(msg.changes) == 0 {
|
||||
client.Reply(RplChannelModeIs(channel))
|
||||
return
|
||||
}
|
||||
|
||||
changes := make(ChannelModeChanges, 0)
|
||||
|
||||
for _, change := range msg.changes {
|
||||
switch change.mode {
|
||||
case BanMask:
|
||||
// TODO add/remove
|
||||
|
||||
for _, banMask := range channel.banList {
|
||||
client.Reply(RplBanList(channel, banMask))
|
||||
}
|
||||
@ -282,12 +293,14 @@ func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch modeOp.op {
|
||||
switch change.op {
|
||||
case Add:
|
||||
channel.flags[modeOp.mode] = true
|
||||
channel.flags[change.mode] = true
|
||||
changes = append(changes, change)
|
||||
|
||||
case Remove:
|
||||
delete(channel.flags, modeOp.mode)
|
||||
delete(channel.flags, change.mode)
|
||||
changes = append(changes, change)
|
||||
}
|
||||
|
||||
case Key:
|
||||
@ -296,34 +309,33 @@ func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch modeOp.op {
|
||||
switch change.op {
|
||||
case Add:
|
||||
if modeOp.arg == "" {
|
||||
if change.arg == "" {
|
||||
// TODO err reply
|
||||
continue
|
||||
}
|
||||
|
||||
channel.key = modeOp.arg
|
||||
channel.key = change.arg
|
||||
changes = append(changes, change)
|
||||
|
||||
case Remove:
|
||||
channel.key = ""
|
||||
changes = append(changes, change)
|
||||
}
|
||||
}
|
||||
|
||||
mmode := ChannelMemberMode(modeOp.mode)
|
||||
switch mmode {
|
||||
case ChannelOperator, Voice:
|
||||
if !channel.ClientIsOperator(client) {
|
||||
client.Reply(ErrChanOPrivIsNeeded(channel))
|
||||
continue
|
||||
}
|
||||
|
||||
if modeOp.arg == "" {
|
||||
if change.arg == "" {
|
||||
// TODO err reply
|
||||
continue
|
||||
}
|
||||
|
||||
target := channel.server.clients[modeOp.arg]
|
||||
target := channel.server.clients[change.arg]
|
||||
if target == nil {
|
||||
// TODO err reply
|
||||
continue
|
||||
@ -334,16 +346,21 @@ func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch modeOp.op {
|
||||
switch change.op {
|
||||
case Add:
|
||||
channel.members[target][mmode] = true
|
||||
channel.members[target][change.mode] = true
|
||||
changes = append(changes, change)
|
||||
|
||||
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) {
|
||||
|
@ -402,6 +402,26 @@ func (change *ModeChange) String() string {
|
||||
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 {
|
||||
BaseCommand
|
||||
nickname string
|
||||
@ -436,27 +456,47 @@ func (cmd *ModeCommand) String() string {
|
||||
return fmt.Sprintf("MODE(nickname=%s, changes=%s)", cmd.nickname, cmd.changes)
|
||||
}
|
||||
|
||||
type ChannelModeOp struct {
|
||||
type ChannelModeChange struct {
|
||||
mode ChannelMode
|
||||
op ModeOp
|
||||
arg string
|
||||
}
|
||||
|
||||
func (op *ChannelModeOp) String() string {
|
||||
func (op *ChannelModeChange) String() string {
|
||||
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 {
|
||||
BaseCommand
|
||||
channel string
|
||||
modeOps []ChannelModeOp
|
||||
changes ChannelModeChanges
|
||||
}
|
||||
|
||||
// MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
|
||||
func NewChannelModeCommand(args []string) (editableCommand, error) {
|
||||
cmd := &ChannelModeCommand{
|
||||
channel: args[0],
|
||||
modeOps: make([]ChannelModeOp, 0),
|
||||
changes: make(ChannelModeChanges, 0),
|
||||
}
|
||||
args = args[1:]
|
||||
|
||||
@ -472,18 +512,18 @@ func NewChannelModeCommand(args []string) (editableCommand, error) {
|
||||
|
||||
skipArgs := 1
|
||||
for _, mode := range modeArg {
|
||||
modeOp := ChannelModeOp{
|
||||
change := ChannelModeChange{
|
||||
mode: ChannelMode(mode),
|
||||
op: op,
|
||||
}
|
||||
switch modeOp.mode {
|
||||
switch change.mode {
|
||||
case Key, BanMask, ExceptionMask, InviteMask, UserLimit:
|
||||
if len(args) > skipArgs {
|
||||
modeOp.arg = args[skipArgs]
|
||||
change.arg = args[skipArgs]
|
||||
skipArgs += 1
|
||||
}
|
||||
}
|
||||
cmd.modeOps = append(cmd.modeOps, modeOp)
|
||||
cmd.changes = append(cmd.changes, change)
|
||||
}
|
||||
args = args[skipArgs:]
|
||||
}
|
||||
@ -492,7 +532,7 @@ func NewChannelModeCommand(args []string) (editableCommand, error) {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -173,24 +173,23 @@ const (
|
||||
ServerNotice UserMode = 's'
|
||||
WallOps UserMode = 'w'
|
||||
|
||||
Anonymous ChannelMode = 'a' // flag
|
||||
BanMask ChannelMode = 'b' // arg
|
||||
ExceptionMask ChannelMode = 'e' // arg
|
||||
InviteMask ChannelMode = 'I' // arg
|
||||
InviteOnly ChannelMode = 'i' // flag
|
||||
Key ChannelMode = 'k' // flag arg
|
||||
Moderated ChannelMode = 'm' // flag
|
||||
NoOutside ChannelMode = 'n' // flag
|
||||
OpOnlyTopic ChannelMode = 't' // flag
|
||||
Private ChannelMode = 'p' // flag
|
||||
Quiet ChannelMode = 'q' // flag
|
||||
ReOp ChannelMode = 'r' // flag
|
||||
Secret ChannelMode = 's' // flag
|
||||
UserLimit ChannelMode = 'l' // flag arg
|
||||
|
||||
ChannelCreator ChannelMemberMode = 'O' // flag
|
||||
ChannelOperator ChannelMemberMode = 'o' // arg
|
||||
Voice ChannelMemberMode = 'v' // arg
|
||||
Anonymous ChannelMode = 'a' // flag
|
||||
BanMask ChannelMode = 'b' // arg
|
||||
ChannelCreator ChannelMode = 'O' // flag
|
||||
ChannelOperator ChannelMode = 'o' // arg
|
||||
ExceptionMask ChannelMode = 'e' // arg
|
||||
InviteMask ChannelMode = 'I' // arg
|
||||
InviteOnly ChannelMode = 'i' // flag
|
||||
Key ChannelMode = 'k' // flag arg
|
||||
Moderated ChannelMode = 'm' // flag
|
||||
NoOutside ChannelMode = 'n' // flag
|
||||
OpOnlyTopic ChannelMode = 't' // flag
|
||||
Private ChannelMode = 'p' // flag
|
||||
Quiet ChannelMode = 'q' // flag
|
||||
ReOp ChannelMode = 'r' // flag
|
||||
Secret ChannelMode = 's' // flag
|
||||
UserLimit ChannelMode = 'l' // flag arg
|
||||
Voice ChannelMode = 'v' // arg
|
||||
)
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
@ -207,9 +216,11 @@ func RplTopic(channel *Channel) Reply {
|
||||
"%s :%s", channel.name, channel.topic)
|
||||
}
|
||||
|
||||
// <nick> <channel>
|
||||
// NB: correction in errata
|
||||
func RplInvitingMsg(channel *Channel, invitee *Client) Reply {
|
||||
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 {
|
||||
@ -238,7 +249,7 @@ func RplEndOfWhois(server *Server) Reply {
|
||||
|
||||
func RplChannelModeIs(channel *Channel) Reply {
|
||||
return NewNumericReply(channel.server, RPL_CHANNELMODEIS, "%s %s",
|
||||
channel.name, channel.ModeString())
|
||||
channel, channel.ModeString())
|
||||
}
|
||||
|
||||
// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
|
||||
|
@ -270,7 +270,6 @@ func (m *NickCommand) HandleRegServer(s *Server) {
|
||||
|
||||
client.nick = m.nickname
|
||||
s.clients.Add(client)
|
||||
client.Reply(RplNick(client, m.nickname))
|
||||
s.tryRegister(client)
|
||||
}
|
||||
|
||||
@ -414,22 +413,31 @@ func (m *PrivMsgCommand) HandleServer(s *Server) {
|
||||
func (m *ModeCommand) HandleServer(s *Server) {
|
||||
client := m.Client()
|
||||
target := s.clients[m.nickname]
|
||||
if client == target {
|
||||
for _, change := range m.changes {
|
||||
if change.mode == Invisible {
|
||||
switch change.op {
|
||||
case Add:
|
||||
client.invisible = true
|
||||
case Remove:
|
||||
client.invisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
client.Reply(RplUModeIs(s, client))
|
||||
// TODO other auth
|
||||
if client != target {
|
||||
client.Reply(ErrUsersDontMatch(s))
|
||||
return
|
||||
}
|
||||
|
||||
client.Reply(ErrUsersDontMatch(s))
|
||||
changes := make(ModeChanges, 0)
|
||||
|
||||
for _, change := range m.changes {
|
||||
if change.mode == Invisible {
|
||||
switch change.op {
|
||||
case Add:
|
||||
client.invisible = true
|
||||
changes = append(changes, change)
|
||||
|
||||
case Remove:
|
||||
client.invisible = false
|
||||
changes = append(changes, change)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(changes) > 0 {
|
||||
client.Reply(RplMode(client, changes))
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
type ModeOp rune
|
||||
|
||||
func (op ModeOp) String() string {
|
||||
return string(op)
|
||||
}
|
||||
|
||||
// user mode flags
|
||||
type UserMode rune
|
||||
|
||||
@ -37,9 +41,6 @@ func (mode ChannelMode) String() string {
|
||||
return fmt.Sprintf("%c", mode)
|
||||
}
|
||||
|
||||
// user-channel mode flags
|
||||
type ChannelMemberMode rune
|
||||
|
||||
type ChannelNameMap map[string]*Channel
|
||||
|
||||
func (channels ChannelNameMap) Add(channel *Channel) error {
|
||||
@ -84,19 +85,19 @@ func (clients ClientNameMap) Remove(client *Client) error {
|
||||
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) {
|
||||
clients[client] = make(ChannelMemberModeSet)
|
||||
clients[client] = make(ChannelModeSet)
|
||||
}
|
||||
|
||||
func (clients ClientSet) Remove(client *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]
|
||||
if !ok {
|
||||
return false
|
||||
|
Loading…
Reference in New Issue
Block a user