Send an IRC listener user their own TOPIC, MODE and KICK once (closes #143) #177
@@ -1019,9 +1019,8 @@ On the IRC listener, `MODE #channel <modes> [values...]` takes several letters
|
|||||||
in one string, e.g. `MODE #general +mt-i` or `MODE #general +ov alice bob`. It
|
in one string, e.g. `MODE #general +mt-i` or `MODE #general +ov alice bob`. It
|
||||||
accepts `i`, `m`, `n`, `s`, `t`, `o` and `v` (with a nick) and `H` (with bits
|
accepts `i`, `m`, `n`, `s`, `t`, `o` and `v` (with a nick) and `H` (with bits
|
||||||
from 1 to 40 when setting), but not `b`, `k` or `l`. A letter whose value is
|
from 1 to 40 when setting), but not `b`, `k` or `l`. A letter whose value is
|
||||||
missing is skipped. The changes made are echoed to the operator's connection and
|
missing is skipped. The changes made are sent to every channel member as one
|
||||||
also sent to every channel member, the operator included, as one `MODE` line,
|
`MODE` line, e.g. `+o-v alice bob`; the operator gets that line once.
|
||||||
e.g. `+o-v alice bob`.
|
|
||||||
|
|
||||||
**Channel errors:**
|
**Channel errors:**
|
||||||
|
|
||||||
|
|||||||
@@ -304,6 +304,15 @@ func (c *Conn) deliverTopicChange(
|
|||||||
msg *db.IRCMessage,
|
msg *db.IRCMessage,
|
||||||
text string,
|
text string,
|
||||||
) {
|
) {
|
||||||
|
c.mu.Lock()
|
||||||
|
nick := c.nick
|
||||||
|
c.mu.Unlock()
|
||||||
|
|
||||||
|
// handleTopic already echoed our own topic change.
|
||||||
|
if msg.From == nick {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
prefix := msg.From + "!" + msg.From + "@*"
|
prefix := msg.From + "!" + msg.From + "@*"
|
||||||
channel := msg.To
|
channel := msg.To
|
||||||
|
|
||||||
@@ -315,6 +324,15 @@ func (c *Conn) deliverKickMsg(
|
|||||||
msg *db.IRCMessage,
|
msg *db.IRCMessage,
|
||||||
text string,
|
text string,
|
||||||
) {
|
) {
|
||||||
|
c.mu.Lock()
|
||||||
|
nick := c.nick
|
||||||
|
c.mu.Unlock()
|
||||||
|
|
||||||
|
// handleKick already echoed our own kick.
|
||||||
|
if msg.From == nick {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
prefix := msg.From + "!" + msg.From + "@*"
|
prefix := msg.From + "!" + msg.From + "@*"
|
||||||
channel := msg.To
|
channel := msg.To
|
||||||
|
|
||||||
@@ -377,6 +395,15 @@ func (c *Conn) deliverMode(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.mu.Lock()
|
||||||
|
nick := c.nick
|
||||||
|
c.mu.Unlock()
|
||||||
|
|
||||||
|
// applyChannelModes already echoed our own mode change.
|
||||||
|
if msg.From == nick {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
prefix := msg.From + "!" + msg.From + "@*"
|
prefix := msg.From + "!" + msg.From + "@*"
|
||||||
|
|
||||||
params := slices.Concat(
|
params := slices.Concat(
|
||||||
|
|||||||
@@ -292,3 +292,70 @@ func TestRelayNickDiffersOnlyInCase(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestRelayOwnChangeOnce checks that an IRC listener user who
|
||||||
|
// sets a topic, changes a channel mode or kicks gets one TOPIC,
|
||||||
|
// MODE or KICK line for it, and so does the other member.
|
||||||
|
func TestRelayOwnChangeOnce(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for _, step := range []struct {
|
||||||
|
command, line string
|
||||||
|
}{
|
||||||
|
{"TOPIC", "TOPIC #dup :new topic"},
|
||||||
|
{"MODE", "MODE #dup +m"},
|
||||||
|
{"KICK", "KICK #dup carl :bye"},
|
||||||
|
} {
|
||||||
|
t.Run(step.command, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
env := newTestEnv(t)
|
||||||
|
|
||||||
|
alice := env.dial(t)
|
||||||
|
alice.register("alice")
|
||||||
|
alice.joinAndDrain("#dup")
|
||||||
|
|
||||||
|
carl := env.dial(t)
|
||||||
|
carl.register("carl")
|
||||||
|
carl.joinAndDrain("#dup")
|
||||||
|
|
||||||
|
alice.send(step.line)
|
||||||
|
|
||||||
|
// The reply to alice's next command comes after her
|
||||||
|
// command has finished, so her own copy comes before it.
|
||||||
|
beforePong := alice.sendAndExpect("PING :done", "PONG")
|
||||||
|
|
||||||
|
// The relay sends a user's queue in order, so a message
|
||||||
|
// queued now arrives after any relayed copy.
|
||||||
|
for nick, client := range map[string]*testClient{
|
||||||
|
"alice": alice, "carl": carl,
|
||||||
|
} {
|
||||||
|
env.queueMessage(
|
||||||
|
t, nick, "PRIVMSG", "dave", nick,
|
||||||
|
[]string{"after the change"},
|
||||||
|
)
|
||||||
|
|
||||||
|
lines := client.readUntil(func(l string) bool {
|
||||||
|
return strings.HasSuffix(l, " :after the change")
|
||||||
|
})
|
||||||
|
|
||||||
|
if client == alice {
|
||||||
|
lines = slices.Concat(beforePong, lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
var changes []string
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.Contains(line, " "+step.command+" #dup ") {
|
||||||
|
changes = append(changes, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(changes) != 1 {
|
||||||
|
t.Errorf("%s got %q, want one %s line",
|
||||||
|
nick, changes, step.command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user