feat(feedback): show the sender's interface language and connector bot in admin
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m7s

In the console feedback detail, show the sender's interface language (account
preferred_language) always, and — for a message that arrived through an external
connector (currently Telegram) — the bot they last used (en/ru, from the
account's service_language).
This commit is contained in:
Ilia Denisov
2026-06-15 13:07:47 +02:00
parent 1ae43080ec
commit 2a4ce319d9
4 changed files with 33 additions and 19 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ func TestRendererRendersEveryPage(t *testing.T) {
{"complaints", ComplaintsView{Items: []ComplaintRow{{ID: "c1", Word: "qi", Status: "open"}}, Status: "open", Pager: NewPager(1, 50, 1)}, "qi"}, {"complaints", ComplaintsView{Items: []ComplaintRow{{ID: "c1", Word: "qi", Status: "open"}}, Status: "open", Pager: NewPager(1, 50, 1)}, "qi"},
{"messages", MessagesView{Items: []MessageRow{{ID: "m1", SenderID: "a1", SenderName: "Kaya", Source: "telegram", Body: "good luck", GameID: "g1"}}, Pager: NewPager(1, 50, 1)}, "good luck"}, {"messages", MessagesView{Items: []MessageRow{{ID: "m1", SenderID: "a1", SenderName: "Kaya", Source: "telegram", Body: "good luck", GameID: "g1"}}, Pager: NewPager(1, 50, 1)}, "good luck"},
{"feedback", FeedbackView{Items: []FeedbackRow{{ID: "f1", AccountID: "a1", SenderName: "Kaya", Source: "telegram", Channel: "web", HasAttachment: true, Replied: true}}, Status: "unread", Pager: NewPager(1, 50, 1)}, "replied"}, {"feedback", FeedbackView{Items: []FeedbackRow{{ID: "f1", AccountID: "a1", SenderName: "Kaya", Source: "telegram", Channel: "web", HasAttachment: true, Replied: true}}, Status: "unread", Pager: NewPager(1, 50, 1)}, "replied"},
{"feedback_detail", FeedbackDetailView{ID: "f1", AccountID: "a1", SenderName: "Kaya", Channel: "ios", Body: "please fix the board", HasAttachment: true, AttachmentName: "shot.png", IsImage: true, Banned: true}, "please fix the board"}, {"feedback_detail", FeedbackDetailView{ID: "f1", AccountID: "a1", SenderName: "Kaya", Channel: "telegram", InterfaceLanguage: "en", BotLanguage: "ru", Body: "please fix the board", HasAttachment: true, AttachmentName: "shot.png", IsImage: true, Banned: true}, "bot: ru"},
{"complaint_detail", ComplaintDetailView{ID: "c1", Word: "qi", Variant: "scrabble_en"}, "Resolve"}, {"complaint_detail", ComplaintDetailView{ID: "c1", Word: "qi", Variant: "scrabble_en"}, "Resolve"},
{"dictionary", DictionaryView{ActiveVersion: "v1.0.0", Variants: []VariantVersions{{Variant: "scrabble_en", Versions: []string{"v1.0.0"}}}, Changes: []DictChangeRow{{Variant: "scrabble_en", Word: "qi", Action: "add"}}}, "Update dictionaries"}, {"dictionary", DictionaryView{ActiveVersion: "v1.0.0", Variants: []VariantVersions{{Variant: "scrabble_en", Versions: []string{"v1.0.0"}}}, Changes: []DictChangeRow{{Variant: "scrabble_en", Word: "qi", Action: "add"}}}, "Update dictionaries"},
{"dictionary_preview", DictionaryPreviewView{Version: "v1.1.0", Token: "0123456789abcdef0123456789abcdef", ActiveVersion: "v1.0.0", Variants: []VariantDiffRow{{Variant: "scrabble_en", AddedCount: 2, RemovedCount: 1, AddedSample: []string{"qi", "za"}, RemovedSample: []string{"xqz"}, RemovedTruncated: true}}}, "v1.1.0"}, {"dictionary_preview", DictionaryPreviewView{Version: "v1.1.0", Token: "0123456789abcdef0123456789abcdef", ActiveVersion: "v1.0.0", Variants: []VariantDiffRow{{Variant: "scrabble_en", AddedCount: 2, RemovedCount: 1, AddedSample: []string{"qi", "za"}, RemovedSample: []string{"xqz"}, RemovedTruncated: true}}}, "v1.1.0"},
@@ -5,7 +5,8 @@
<section class="panel"><h2>Message</h2> <section class="panel"><h2>Message</h2>
<ul class="kv"> <ul class="kv">
<li><b>From</b> <a href="/_gm/users/{{.AccountID}}">{{.SenderName}}</a> ({{.Source}})</li> <li><b>From</b> <a href="/_gm/users/{{.AccountID}}">{{.SenderName}}</a> ({{.Source}})</li>
<li><b>Channel</b> {{.Channel}}</li> <li><b>Channel</b> {{.Channel}}{{if .BotLanguage}} (bot: {{.BotLanguage}}){{end}}</li>
<li><b>Interface language</b> {{.InterfaceLanguage}}</li>
<li><b>IP</b> {{if .IP}}<code>{{.IP}}</code>{{else}}<span class="note">none</span>{{end}}</li> <li><b>IP</b> {{if .IP}}<code>{{.IP}}</code>{{else}}<span class="note">none</span>{{end}}</li>
<li><b>Filed</b> {{.CreatedAt}}</li> <li><b>Filed</b> {{.CreatedAt}}</li>
<li><b>State</b> {{if .Archived}}archived{{else if .Read}}read{{else}}<span class="warn">unread</span>{{end}}</li> <li><b>State</b> {{if .Archived}}archived{{else if .Read}}read{{else}}<span class="warn">unread</span>{{end}}</li>
+22 -17
View File
@@ -404,21 +404,26 @@ type FeedbackRow struct {
// user-controlled and rendered as plain auto-escaped text. IsImage gates the inline // user-controlled and rendered as plain auto-escaped text. IsImage gates the inline
// <img> preview; Banned shows whether the sender already holds the feedback ban. // <img> preview; Banned shows whether the sender already holds the feedback ban.
type FeedbackDetailView struct { type FeedbackDetailView struct {
ID string ID string
AccountID string AccountID string
SenderName string SenderName string
Source string Source string
Channel string Channel string
IP string // InterfaceLanguage is the sender's interface language (account preference);
Body string // BotLanguage is the connector bot they last used (en/ru), set only for a
HasAttachment bool // message that arrived through an external connector (Telegram).
AttachmentName string InterfaceLanguage string
IsImage bool BotLanguage string
Read bool IP string
Archived bool Body string
Replied bool HasAttachment bool
ReplyBody string AttachmentName string
RepliedAt string IsImage bool
CreatedAt string Read bool
Banned bool Archived bool
Replied bool
ReplyBody string
RepliedAt string
CreatedAt string
Banned bool
} }
@@ -87,6 +87,14 @@ func (s *Server) consoleFeedbackDetail(c *gin.Context) {
if banned, err := s.accounts.HasRole(ctx, m.AccountID, account.RoleFeedbackBanned); err == nil { if banned, err := s.accounts.HasRole(ctx, m.AccountID, account.RoleFeedbackBanned); err == nil {
view.Banned = banned view.Banned = banned
} }
if acc, err := s.accounts.GetByID(ctx, m.AccountID); err == nil {
view.InterfaceLanguage = acc.PreferredLanguage
// The connector bot (the account's last service-language bot) is meaningful only
// for a message that arrived through an external connector — currently Telegram.
if m.Channel == "telegram" && acc.ServiceLanguage != "" {
view.BotLanguage = acc.ServiceLanguage
}
}
s.renderConsole(c, "feedback_detail", "feedback", "Feedback", view) s.renderConsole(c, "feedback_detail", "feedback", "Feedback", view)
} }