Relay INVITE to IRC listener users as an INVITE line (closes #147)
check / check (push) Waiting to run
check / check (push) Waiting to run
An invite for an IRC listener user arrived as a server NOTICE that did not name the inviter, so no IRC client treated it as an invite. The relay now sends it as an INVITE line from the inviter, with the channel taken from the message body, and sends nothing when the body names no channel. The IRC listener now queues the INVITE with the channel as its body, as the HTTP API already did, so an invite from either transport has the same shape. The README's IRC listener section says how an invite arrives. Model: opus-5-5
This commit was merged in pull request #180.
This commit is contained in:
@@ -2719,7 +2719,9 @@ Each line of a multi-line `body` reaches an IRC listener user as its own
|
|||||||
is sent, so it cannot start a line of its own. A mode change made over the HTTP
|
is sent, so it cannot start a line of its own. A mode change made over the HTTP
|
||||||
API reaches IRC listener members as an IRC `MODE` line with its value as a
|
API reaches IRC listener members as an IRC `MODE` line with its value as a
|
||||||
separate parameter, e.g. `MODE #general +k secretpass` or
|
separate parameter, e.g. `MODE #general +k secretpass` or
|
||||||
`MODE #general +o bob`.
|
`MODE #general +o bob`. An `INVITE` from either transport reaches the invited
|
||||||
|
IRC listener user as an IRC `INVITE` line from the inviter, e.g.
|
||||||
|
`:alice!alice@* INVITE bob #general`.
|
||||||
|
|
||||||
### Docker Usage
|
### Docker Usage
|
||||||
|
|
||||||
|
|||||||
@@ -1261,10 +1261,9 @@ func (c *Conn) handleInvite(
|
|||||||
irc.RplInviting, targetNick, channel,
|
irc.RplInviting, targetNick, channel,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Send INVITE notice to target via service fan-out.
|
// Queue the INVITE to the target with the body the HTTP
|
||||||
body, _ := json.Marshal( //nolint:errchkjson
|
// API uses.
|
||||||
[]string{"You have been invited to " + channel},
|
body, _ := json.Marshal([]string{channel}) //nolint:errchkjson
|
||||||
)
|
|
||||||
|
|
||||||
_, _, _ = c.svc.FanOut( //nolint:dogsled // fire-and-forget broadcast
|
_, _, _ = c.svc.FanOut( //nolint:dogsled // fire-and-forget broadcast
|
||||||
ctx, "INVITE", c.nick, targetNick, targetID, 0,
|
ctx, "INVITE", c.nick, targetNick, targetID, 0,
|
||||||
|
|||||||
@@ -1373,6 +1373,42 @@ func TestIntegrationInviteUserOnChannel(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestIntegrationInviteLine checks that an IRC listener user
|
||||||
|
// invited by an IRC listener user, and then over the HTTP
|
||||||
|
// API, gets an INVITE line from the inviter for the channel
|
||||||
|
// each time.
|
||||||
|
func TestIntegrationInviteLine(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
env := newTestEnv(t)
|
||||||
|
|
||||||
|
alice := env.dial(t)
|
||||||
|
alice.register("alice")
|
||||||
|
alice.joinAndDrain("#inv")
|
||||||
|
|
||||||
|
bob := env.dial(t)
|
||||||
|
bob.register("bob")
|
||||||
|
|
||||||
|
alice.sendAndExpect("INVITE bob #inv", " 341 ")
|
||||||
|
|
||||||
|
got := bob.readLine()
|
||||||
|
want := ":alice!alice@* INVITE bob #inv"
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("from the IRC listener: got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The HTTP API queues an INVITE with the channel as its body.
|
||||||
|
env.queueMessage(t, "bob", "INVITE", "carol", "bob", []string{"#inv"})
|
||||||
|
|
||||||
|
got = bob.readLine()
|
||||||
|
want = ":carol!carol@* INVITE bob #inv"
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("over the HTTP API: got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestIntegrationModeSecretList checks that LIST shows a +s
|
// TestIntegrationModeSecretList checks that LIST shows a +s
|
||||||
// channel only to its members.
|
// channel only to its members.
|
||||||
func TestIntegrationModeSecretList(t *testing.T) {
|
func TestIntegrationModeSecretList(t *testing.T) {
|
||||||
|
|||||||
@@ -358,16 +358,24 @@ func (c *Conn) deliverKickMsg(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// deliverInviteMsg sends an INVITE notification.
|
// deliverInviteMsg sends an INVITE line from the inviter
|
||||||
|
// for the channel named in text, or nothing when text names
|
||||||
|
// no channel.
|
||||||
func (c *Conn) deliverInviteMsg(
|
func (c *Conn) deliverInviteMsg(
|
||||||
_ *db.IRCMessage,
|
msg *db.IRCMessage,
|
||||||
text string,
|
text string,
|
||||||
) {
|
) {
|
||||||
|
if text == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
nick := c.nick
|
nick := c.nick
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
|
|
||||||
c.sendFromServer("NOTICE", nick, text)
|
prefix := msg.From + "!" + msg.From + "@*"
|
||||||
|
|
||||||
|
c.send(FormatMessage(prefix, "INVITE", nick, text))
|
||||||
}
|
}
|
||||||
|
|
||||||
// deliverWallops sends a WALLOPS notification.
|
// deliverWallops sends a WALLOPS notification.
|
||||||
|
|||||||
@@ -211,6 +211,38 @@ func TestRelayModeParameters(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestRelayInviteWithoutChannel checks that an INVITE whose
|
||||||
|
// body names no channel sends the invited IRC listener user
|
||||||
|
// no INVITE line.
|
||||||
|
func TestRelayInviteWithoutChannel(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
env := newTestEnv(t)
|
||||||
|
|
||||||
|
bob := env.dial(t)
|
||||||
|
bob.register("bob")
|
||||||
|
|
||||||
|
// A nil body is stored as JSON null, which is not an array.
|
||||||
|
for _, body := range [][]string{nil, {}, {""}} {
|
||||||
|
env.queueMessage(t, "bob", "INVITE", "carol", "bob", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The relay sends a user's queue in order, so this arrives
|
||||||
|
// after anything sent for the INVITEs above.
|
||||||
|
env.queueMessage(t, "bob", "PRIVMSG", "carol", "bob",
|
||||||
|
[]string{"the end"})
|
||||||
|
|
||||||
|
lines := bob.readUntil(func(l string) bool {
|
||||||
|
return strings.HasSuffix(l, " :the end")
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.Contains(line, " INVITE ") {
|
||||||
|
t.Errorf("got %q, want no INVITE line", line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestRelayLineBreaks checks that a CR, LF or NUL in a stored
|
// TestRelayLineBreaks checks that a CR, LF or NUL in a stored
|
||||||
// body line, key or mask never reaches an IRC listener user
|
// body line, key or mask never reaches an IRC listener user
|
||||||
// as a line break, so no extra line arrives, and that a body
|
// as a line break, so no extra line arrives, and that a body
|
||||||
|
|||||||
Reference in New Issue
Block a user