Skip to content

Commit 0f8ea15

Browse files
authored
Merge pull request #213 from beeper/highest/plat-37893
fix: supported reactions not advertised correctly
2 parents 3ff4e26 + 046eddb commit 0f8ea15

4 files changed

Lines changed: 75 additions & 1 deletion

File tree

pkg/connector/connector.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (lc *LineConnector) Start(ctx context.Context) error {
4444
}
4545

4646
func (lc *LineConnector) GetBridgeInfoVersion() (info, capabilities int) {
47-
return 1, 1
47+
return 1, 2
4848
}
4949

5050
func (lc *LineConnector) GetCapabilities() *bridgev2.NetworkGeneralCapabilities {

pkg/connector/reaction.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ import (
55
"errors"
66
"fmt"
77
"net/url"
8+
"slices"
89
"strconv"
910
"strings"
1011
"time"
1112

13+
"go.mau.fi/util/variationselector"
14+
1215
"maunium.net/go/mautrix/bridgev2"
1316
"maunium.net/go/mautrix/bridgev2/database"
1417
"maunium.net/go/mautrix/bridgev2/networkid"
@@ -174,6 +177,26 @@ var lineEmojiReactionURLs = map[string]string{
174177
"9": lineSticonURL(lineOriginalEmojiProductID, "211"),
175178
}
176179

180+
// lineAllowedReactions is the Matrix-facing form of the outbound LINE reaction
181+
// map. Room capability hashes depend on slice order, so keep this list sorted.
182+
var lineAllowedReactions = func() []string {
183+
reactions := make([]string, 0, len(lineEmojiReactionURLs))
184+
for reaction := range lineEmojiReactionURLs {
185+
// The send lookup normalizes keycaps to bare digits. Restore their emoji
186+
// form before advertising them to clients.
187+
if len(reaction) == 1 && reaction[0] >= '0' && reaction[0] <= '9' {
188+
reaction += "\u20E3"
189+
}
190+
reactions = append(reactions, variationselector.Add(reaction))
191+
}
192+
slices.Sort(reactions)
193+
return reactions
194+
}()
195+
196+
func getLineAllowedReactions() []string {
197+
return slices.Clone(lineAllowedReactions)
198+
}
199+
177200
func lineSticonURL(productID, emojiID string) string {
178201
return fmt.Sprintf("https://stickershop.line-scdn.net/sticonshop/v1/sticon/%s/android/%s.png", productID, emojiID)
179202
}

pkg/connector/reaction_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package connector
22

33
import (
4+
"context"
45
"errors"
6+
"slices"
57
"testing"
68
"time"
79

@@ -10,6 +12,53 @@ import (
1012
"maunium.net/go/mautrix/event"
1113
)
1214

15+
func TestCapabilitiesAdvertiseSupportedReactions(t *testing.T) {
16+
infoVersion, capabilityVersion := (&LineConnector{}).GetBridgeInfoVersion()
17+
if infoVersion != 1 || capabilityVersion != 2 {
18+
t.Fatalf("bridge info/capability versions = %d/%d, want 1/2", infoVersion, capabilityVersion)
19+
}
20+
21+
caps := (&LineClient{}).GetCapabilities(context.Background(), nil)
22+
if caps.Reaction != event.CapLevelPartialSupport {
23+
t.Fatalf("Reaction = %d, want partial support", caps.Reaction)
24+
}
25+
if caps.ReactionCount != 1 {
26+
t.Fatalf("ReactionCount = %d, want 1", caps.ReactionCount)
27+
}
28+
if len(caps.AllowedReactions) != len(lineEmojiReactionURLs) {
29+
t.Fatalf("AllowedReactions has %d entries, want %d", len(caps.AllowedReactions), len(lineEmojiReactionURLs))
30+
}
31+
if !slices.IsSorted(caps.AllowedReactions) {
32+
t.Fatal("AllowedReactions must be sorted so room capability IDs are stable")
33+
}
34+
35+
seen := make(map[string]struct{}, len(caps.AllowedReactions))
36+
for _, reaction := range caps.AllowedReactions {
37+
if _, duplicate := seen[reaction]; duplicate {
38+
t.Fatalf("AllowedReactions contains duplicate %q", reaction)
39+
}
40+
seen[reaction] = struct{}{}
41+
if _, ok := linePaidReactionForMatrixEmoji(reaction); !ok {
42+
t.Fatalf("advertised reaction %q is not accepted by the LINE reaction mapper", reaction)
43+
}
44+
}
45+
46+
for _, supported := range []string{"\U0001F44D\uFE0F", "9\uFE0F\u20E3"} {
47+
if !slices.Contains(caps.AllowedReactions, supported) {
48+
t.Fatalf("supported reaction %q is not advertised", supported)
49+
}
50+
}
51+
if slices.Contains(caps.AllowedReactions, "\U0001F625") {
52+
t.Fatal("unsupported reaction 😥 must not be advertised")
53+
}
54+
55+
caps.AllowedReactions[0] = "mutated"
56+
freshCaps := (&LineClient{}).GetCapabilities(context.Background(), nil)
57+
if freshCaps.AllowedReactions[0] == "mutated" {
58+
t.Fatal("GetCapabilities returned a shared mutable reaction list")
59+
}
60+
}
61+
1362
func TestNormalizeMatrixReactionKey(t *testing.T) {
1463
tests := map[string]string{
1564
"9\uFE0F\u20E3": "9",

pkg/connector/userinfo.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ func (lc *LineClient) GetCapabilities(ctx context.Context, portal *bridgev2.Port
4848
MaxTextLength: 5000,
4949
Reply: event.CapLevelFullySupported,
5050
Reaction: event.CapLevelPartialSupport,
51+
ReactionCount: 1,
52+
AllowedReactions: getLineAllowedReactions(),
5153
ReadReceipts: true,
5254
Delete: event.CapLevelFullySupported,
5355
DeleteMaxAge: &jsontime.Seconds{Duration: 24 * time.Hour},

0 commit comments

Comments
 (0)