Send an IRC listener user their own TOPIC, MODE and KICK once (closes #143)
check / check (push) Waiting to run
check / check (push) Waiting to run
The service sends a TOPIC, MODE or KICK to every channel member, the issuer included, and the IRC listener also echoes it to the issuer's connection, so an IRC listener user got each of their own changes twice. The relay now drops one whose sender is the connection's own nick, as it already does for PRIVMSG, NOTICE, JOIN, PART, NICK and QUIT. The direct echo stays: it carries the real hostmask and arrives in order. The README MODE section no longer describes a second copy. Model: opus-5-5
This commit was merged in pull request #177.
This commit is contained in:
@@ -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
|
||||
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
|
||||
missing is skipped. The changes made are echoed to the operator's connection and
|
||||
also sent to every channel member, the operator included, as one `MODE` line,
|
||||
e.g. `+o-v alice bob`.
|
||||
missing is skipped. The changes made are sent to every channel member as one
|
||||
`MODE` line, e.g. `+o-v alice bob`; the operator gets that line once.
|
||||
|
||||
**Channel errors:**
|
||||
|
||||
|
||||
@@ -304,6 +304,15 @@ func (c *Conn) deliverTopicChange(
|
||||
msg *db.IRCMessage,
|
||||
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 + "@*"
|
||||
channel := msg.To
|
||||
|
||||
@@ -315,6 +324,15 @@ func (c *Conn) deliverKickMsg(
|
||||
msg *db.IRCMessage,
|
||||
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 + "@*"
|
||||
channel := msg.To
|
||||
|
||||
@@ -377,6 +395,15 @@ func (c *Conn) deliverMode(
|
||||
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 + "@*"
|
||||
|
||||
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