fix(discord): prevent duplicate link expansion and add regex tests
Address Copilot review feedback: - Move resolveDiscordRefs(content) before the referenced message concatenation to prevent message links in quoted replies from being expanded twice. - Add unit tests for channelRefRe and msgLinkRe regex patterns, covering valid/invalid inputs and the 3-link cap.
This commit is contained in:
parent
38263333ed
commit
e0616362fe
2 changed files with 102 additions and 1 deletions
|
|
@ -345,6 +345,10 @@ func (c *DiscordChannel) handleMessage(s *discordgo.Session, m *discordgo.Messag
|
||||||
content = c.stripBotMention(content)
|
content = c.stripBotMention(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve Discord refs in main content before concatenation to avoid
|
||||||
|
// double-expanding links that appear in the referenced message.
|
||||||
|
content = c.resolveDiscordRefs(s, content, m.GuildID)
|
||||||
|
|
||||||
// Prepend referenced (quoted) message content if this is a reply
|
// Prepend referenced (quoted) message content if this is a reply
|
||||||
if m.MessageReference != nil && m.ReferencedMessage != nil {
|
if m.MessageReference != nil && m.ReferencedMessage != nil {
|
||||||
refContent := m.ReferencedMessage.Content
|
refContent := m.ReferencedMessage.Content
|
||||||
|
|
@ -358,7 +362,6 @@ func (c *DiscordChannel) handleMessage(s *discordgo.Session, m *discordgo.Messag
|
||||||
refAuthor, refContent, content)
|
refAuthor, refContent, content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content = c.resolveDiscordRefs(s, content, m.GuildID)
|
|
||||||
|
|
||||||
senderID := m.Author.ID
|
senderID := m.Author.ID
|
||||||
|
|
||||||
|
|
|
||||||
98
pkg/channels/discord/discord_resolve_test.go
Normal file
98
pkg/channels/discord/discord_resolve_test.go
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
package discord
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestChannelRefRegex(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
wantID string
|
||||||
|
wantOK bool
|
||||||
|
}{
|
||||||
|
{"basic channel ref", "<#123456789>", "123456789", true},
|
||||||
|
{"long id", "<#9876543210123456>", "9876543210123456", true},
|
||||||
|
{"no match plain text", "hello world", "", false},
|
||||||
|
{"no match partial", "<#>", "", false},
|
||||||
|
{"no match letters", "<#abc>", "", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
matches := channelRefRe.FindStringSubmatch(tt.input)
|
||||||
|
if tt.wantOK {
|
||||||
|
if len(matches) < 2 || matches[1] != tt.wantID {
|
||||||
|
t.Errorf("channelRefRe(%q) = %v, want ID %q", tt.input, matches, tt.wantID)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(matches) >= 2 {
|
||||||
|
t.Errorf("channelRefRe(%q) should not match, got %v", tt.input, matches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMsgLinkRegex(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
wantGuild string
|
||||||
|
wantChan string
|
||||||
|
wantMsg string
|
||||||
|
wantOK bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"discord.com link",
|
||||||
|
"https://discord.com/channels/111/222/333",
|
||||||
|
"111", "222", "333", true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"discordapp.com link",
|
||||||
|
"https://discordapp.com/channels/111/222/333",
|
||||||
|
"111", "222", "333", true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"real world ids",
|
||||||
|
"check this https://discord.com/channels/9000000000000001/9000000000000002/9000000000000003 please",
|
||||||
|
"9000000000000001", "9000000000000002", "9000000000000003", true,
|
||||||
|
},
|
||||||
|
{"no match http", "http://discord.com/channels/1/2/3", "", "", "", false},
|
||||||
|
{"no match missing segment", "https://discord.com/channels/1/2", "", "", "", false},
|
||||||
|
{"no match plain text", "hello world", "", "", "", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
matches := msgLinkRe.FindStringSubmatch(tt.input)
|
||||||
|
if tt.wantOK {
|
||||||
|
if len(matches) < 4 {
|
||||||
|
t.Fatalf("msgLinkRe(%q) didn't match, want guild=%s chan=%s msg=%s",
|
||||||
|
tt.input, tt.wantGuild, tt.wantChan, tt.wantMsg)
|
||||||
|
}
|
||||||
|
if matches[1] != tt.wantGuild || matches[2] != tt.wantChan || matches[3] != tt.wantMsg {
|
||||||
|
t.Errorf("msgLinkRe(%q) = guild=%s chan=%s msg=%s, want %s/%s/%s",
|
||||||
|
tt.input, matches[1], matches[2], matches[3],
|
||||||
|
tt.wantGuild, tt.wantChan, tt.wantMsg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(matches) >= 4 {
|
||||||
|
t.Errorf("msgLinkRe(%q) should not match, got %v", tt.input, matches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMsgLinkRegex_MultipleMatches(t *testing.T) {
|
||||||
|
input := "see https://discord.com/channels/1/2/3 and https://discord.com/channels/4/5/6 and https://discord.com/channels/7/8/9 and https://discord.com/channels/10/11/12"
|
||||||
|
matches := msgLinkRe.FindAllStringSubmatch(input, 3)
|
||||||
|
if len(matches) != 3 {
|
||||||
|
t.Fatalf("expected 3 matches (capped), got %d", len(matches))
|
||||||
|
}
|
||||||
|
// Verify the 3rd match is 7/8/9 (not 10/11/12)
|
||||||
|
if matches[2][1] != "7" || matches[2][2] != "8" || matches[2][3] != "9" {
|
||||||
|
t.Errorf("3rd match = %v, want guild=7 chan=8 msg=9", matches[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue