@@ -50,11 +50,12 @@ var cardChartTypeNames = map[string]string{
5050type interactiveConverter struct {}
5151
5252func (interactiveConverter ) Convert (ctx * ConvertContext ) string {
53- return convertCard (ctx .RawContent )
53+ return convertCard (ctx .RawContent , ctx . Mentions )
5454}
5555
5656// convertCard converts a raw interactive/card message content JSON to human-readable string.
57- func convertCard (raw string ) string {
57+ // mentions is the raw mentions array from the API response; pass nil when not available.
58+ func convertCard (raw string , mentions []interface {}) string {
5859 var parsed cardObj
5960 if err := json .Unmarshal ([]byte (raw ), & parsed ); err != nil {
6061 return "[interactive card]"
@@ -63,11 +64,19 @@ func convertCard(raw string) string {
6364 // raw_card_content format: outer JSON has "json_card" string field
6465 if jsonCard , ok := parsed ["json_card" ].(string ); ok {
6566 c := & cardConverter {mode : cardModeConcise }
66- if att , ok := parsed ["json_attachment" ].(string ); ok && att != "" {
67- var attObj cardObj
68- if json .Unmarshal ([]byte (att ), & attObj ) == nil {
69- c .attachment = attObj
67+ switch att := parsed ["json_attachment" ].(type ) {
68+ case string :
69+ if att != "" {
70+ var attObj cardObj
71+ if json .Unmarshal ([]byte (att ), & attObj ) == nil {
72+ c .attachment = attObj
73+ }
7074 }
75+ case cardObj :
76+ c .attachment = att
77+ }
78+ if len (mentions ) > 0 {
79+ c .mentionsByKey = buildMentionsByKey (mentions )
7180 }
7281 schema := 0
7382 if s , ok := parsed ["card_schema" ].(float64 ); ok {
@@ -84,6 +93,22 @@ func convertCard(raw string) string {
8493 return convertLegacyCard (parsed )
8594}
8695
96+ // buildMentionsByKey indexes the mentions array by key for O(1) lookup in convertAt.
97+ func buildMentionsByKey (mentions []interface {}) map [string ]map [string ]interface {} {
98+ m := make (map [string ]map [string ]interface {}, len (mentions ))
99+ for _ , raw := range mentions {
100+ item , ok := raw .(map [string ]interface {})
101+ if ! ok {
102+ continue
103+ }
104+ key , _ := item ["key" ].(string )
105+ if key != "" {
106+ m [key ] = item
107+ }
108+ }
109+ return m
110+ }
111+
87112// ── Legacy converter ──────────────────────────────────────────────────────────
88113
89114func convertLegacyCard (parsed cardObj ) string {
@@ -158,8 +183,9 @@ func legacyExtractTexts(elements []interface{}, out *[]string) {
158183// ── CardConverter ─────────────────────────────────────────────────────────────
159184
160185type cardConverter struct {
161- mode cardMode
162- attachment cardObj
186+ mode cardMode
187+ attachment cardObj
188+ mentionsByKey map [string ]map [string ]interface {}
163189}
164190
165191func (c * cardConverter ) convert (jsonCard string , hintSchema int ) string {
@@ -1403,26 +1429,52 @@ func (c *cardConverter) convertAt(prop cardObj) string {
14031429 }
14041430 userName := ""
14051431 actualUserID := ""
1432+ fromMentions := false
14061433 if c .attachment != nil {
14071434 if atUsers , ok := c .attachment ["at_users" ].(cardObj ); ok {
14081435 if userInfo , ok := atUsers [userID ].(cardObj ); ok {
14091436 userName , _ = userInfo ["content" ].(string )
14101437 actualUserID , _ = userInfo ["user_id" ].(string )
1438+ // When the backend populates mention_key (raw_card_content path), use
1439+ // mentions[] for the canonical name and the reading-app open_id, which is
1440+ // more accurate than the origKey-stored user_id in at_users.
1441+ if mentionKey , _ := userInfo ["mention_key" ].(string ); mentionKey != "" {
1442+ if mention , ok := c .mentionsByKey [mentionKey ]; ok {
1443+ if name , _ := mention ["name" ].(string ); name != "" {
1444+ userName = name
1445+ }
1446+ if id := extractMentionOpenId (mention ["id" ]); id != "" {
1447+ actualUserID = id
1448+ fromMentions = true
1449+ }
1450+ }
1451+ }
14111452 }
14121453 }
14131454 }
14141455 if userName != "" {
14151456 if c .mode == cardModeDetailed {
14161457 if actualUserID != "" {
1417- return fmt .Sprintf ("@%s(user_id:%s)" , userName , actualUserID )
1458+ label := "user_id"
1459+ if fromMentions {
1460+ label = "open_id"
1461+ }
1462+ return fmt .Sprintf ("@%s(%s:%s)" , userName , label , actualUserID )
14181463 }
14191464 return fmt .Sprintf ("@%s(open_id:%s)" , userName , userID )
14201465 }
1421- return "@" + userName
1466+ if fromMentions && actualUserID != "" {
1467+ return fmt .Sprintf ("@%s(%s)" , userName , actualUserID )
1468+ }
1469+ return fmt .Sprintf ("@%s(%s)" , userName , userID )
14221470 }
14231471 if c .mode == cardModeDetailed {
14241472 if actualUserID != "" {
1425- return fmt .Sprintf ("@user(user_id:%s)" , actualUserID )
1473+ label := "user_id"
1474+ if fromMentions {
1475+ label = "open_id"
1476+ }
1477+ return fmt .Sprintf ("@user(%s:%s)" , label , actualUserID )
14261478 }
14271479 return fmt .Sprintf ("@user(open_id:%s)" , userID )
14281480 }
0 commit comments