-
Notifications
You must be signed in to change notification settings - Fork 7
feat(messaging/slack): Assistant branding + DataTableBlock skills (#565) #571
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -267,6 +267,10 @@ type SlackConfig struct { | |
| ReconnectBaseDelay time.Duration `mapstructure:"reconnect_base_delay"` | ||
| ReconnectMaxDelay time.Duration `mapstructure:"reconnect_max_delay"` | ||
|
|
||
| // Branding for Assistant status (paid workspaces). | ||
| DisplayName string `mapstructure:"display_name,omitempty"` | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: |
||
| IconEmoji string `mapstructure:"icon_emoji,omitempty"` | ||
|
|
||
| // Multi-bot configuration. When non-empty, takes precedence over top-level credentials. | ||
| Bots []SlackBotConfig `mapstructure:"bots"` | ||
| } | ||
|
|
@@ -287,6 +291,10 @@ type SlackBotConfig struct { | |
| AllowDMFrom []string `mapstructure:"allow_dm_from,omitempty"` | ||
| AllowGroupFrom []string `mapstructure:"allow_group_from,omitempty"` | ||
|
|
||
| // Per-bot branding override (falls back to platform-level when empty). | ||
| DisplayName string `mapstructure:"display_name,omitempty"` | ||
| IconEmoji string `mapstructure:"icon_emoji,omitempty"` | ||
|
|
||
| STTConfig `mapstructure:",squash"` | ||
| TTSConfig `mapstructure:",squash"` | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,45 +21,60 @@ func (c *SlackConn) sendSkillsList(ctx context.Context, env *events.Envelope) er | |
| } | ||
|
|
||
| groups := messaging.GroupSkillsBySource(d.Skills) | ||
| pages := messaging.PaginateSkillGroups(groups, messaging.SkillsPerPage) | ||
| // page=1, total=1: non-paginated display, suppresses "Part X/Y" suffix. | ||
| header := messaging.SkillsHeader(d, 1, 1) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After removing pagination,
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P3] 语义哨兵注释建议
header := messaging.SkillsHeader(d, 1, 1) // non-paginated: suppresses "Part X/Y" suffix |
||
|
|
||
| // Build DataTableBlocks — one table per skill group. | ||
| var blocks []slack.Block | ||
| var shown int | ||
| blocks = append(blocks, slack.NewSectionBlock( | ||
| slack.NewTextBlockObject(slack.PlainTextType, header, false, false), nil, nil)) | ||
|
|
||
| for i, g := range groups { | ||
| // Reserve 1 slot for the header SectionBlock above. | ||
| if len(blocks) >= maxBlocksPerMessage-1 { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3 [UNCERTAIN]: 当 group 数量超过 |
||
| break | ||
| } | ||
| blocks = append(blocks, buildSkillGroupTable(g, fmt.Sprintf("skills_%s_%d", g.Source, i))) | ||
| shown = i + 1 | ||
| } | ||
| // Append truncation notice if some groups were omitted (very rare: requires 99+ sources). | ||
| if shown < len(groups) { | ||
| remaining := len(groups) - shown | ||
| blocks = append(blocks, slack.NewSectionBlock( | ||
| slack.NewTextBlockObject(slack.PlainTextType, | ||
| fmt.Sprintf("… and %d more group(s) — use `$skills` for full list", remaining), false, false), | ||
| nil, nil)) | ||
| } | ||
|
|
||
| for i, page := range pages { | ||
| var blocks []slack.Block | ||
| fallback := header + "\n" + formatSkillsPlainText(groups) | ||
| return c.postSkillsMessage(ctx, fallback, blocks) | ||
| } | ||
|
|
||
| header := messaging.SkillsHeader(d, i+1, len(pages)) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] block 总数无硬上限 旧代码有 建议循环后加回 guard: if len(blocks) > 50 {
blocks = blocks[:50]
} |
||
| blocks = append(blocks, slack.NewSectionBlock( | ||
| slack.NewTextBlockObject(slack.PlainTextType, header, false, false), nil, nil)) | ||
|
|
||
| for _, g := range page { | ||
| emoji := messaging.SourceEmoji(g.Source) | ||
|
|
||
| var sb strings.Builder | ||
| fmt.Fprintf(&sb, "*%s %s (%d)*\n", emoji, g.Source, len(g.Entries)) | ||
| for _, s := range g.Entries { | ||
| desc := messaging.TruncateDesc(s.Description) | ||
| fmt.Fprintf(&sb, "• %s — %s\n", s.Name, desc) | ||
| } | ||
| blocks = append(blocks, slack.NewSectionBlock( | ||
| slack.NewTextBlockObject(slack.MarkdownType, sb.String(), false, false), nil, nil)) | ||
|
|
||
| if len(blocks) >= messaging.SkillsBlockSoftLimit { | ||
| break | ||
| } | ||
| } | ||
| // buildSkillGroupTable creates a DataTableBlock for a single skill group. | ||
| func buildSkillGroupTable(g messaging.SkillGroup, blockID string) *slack.DataTableBlock { | ||
| emoji := messaging.SourceEmoji(g.Source) | ||
| caption := fmt.Sprintf("%s %s (%d)", emoji, g.Source, len(g.Entries)) | ||
|
|
||
| if len(blocks) > messaging.SkillsBlockHardLimit { | ||
| blocks = blocks[:messaging.SkillsBlockHardLimit] | ||
| } | ||
| table := slack.NewDataTableBlock(caption, slack.DataTableBlockOptionBlockID(blockID)) | ||
|
|
||
| fallback := header + "\n" + formatSkillsPlainText(page) | ||
| if err := c.postSkillsMessage(ctx, fallback, blocks); err != nil { | ||
| return err | ||
| // Header row. | ||
| table.AddRow(dataTableCell("Name"), dataTableCell("Description")) | ||
|
|
||
| // Data rows. Cap at maxDataTableRows-1 (excluding header) to prevent Slack rejection. | ||
| maxRows := maxDataTableRows - 1 | ||
| for i, s := range g.Entries { | ||
| if i >= maxRows { | ||
| table.AddRow(dataTableCell("..."), dataTableCell(fmt.Sprintf("and %d more", len(g.Entries)-maxRows))) | ||
| break | ||
| } | ||
| table.AddRow(dataTableCell(s.Name), dataTableCell(messaging.TruncateDesc(s.Description))) | ||
| } | ||
|
|
||
| return nil | ||
| return table | ||
| } | ||
|
|
||
| // postSkillsMessageFallback sends skills as plain text when blocks are rejected. | ||
| func (c *SlackConn) postSkillsMessageFallback(ctx context.Context, env *events.Envelope) error { | ||
| d, err := messaging.ExtractSkillsListData(env) | ||
| if err != nil { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -305,6 +305,8 @@ func (m *StatusManager) shortenPaths(s string) string { | |
| } | ||
|
|
||
| // SetAssistantStatus sets the native assistant status text via Slack API. | ||
| // When displayName or iconEmoji are configured, they are included as branding | ||
| // on the status event (Username/IconEmoji fields). | ||
| func (a *Adapter) SetAssistantStatus(ctx context.Context, channelID, threadTS, status string) error { | ||
| if a.client == nil || threadTS == "" { | ||
| return nil | ||
|
|
@@ -315,6 +317,12 @@ func (a *Adapter) SetAssistantStatus(ctx context.Context, channelID, threadTS, s | |
| ThreadTS: threadTS, | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: SetAssistantStatus 现在同时设置 Username/IconEmoji branding,但 godoc 仅描述 status text 职责。建议补充 branding 行为说明,方便后续调用者理解副作用。 |
||
| Status: status, | ||
| } | ||
| if a.displayName != "" { | ||
| params.Username = a.displayName | ||
| } | ||
| if a.iconEmoji != "" { | ||
| params.IconEmoji = a.iconEmoji | ||
| } | ||
|
|
||
| return a.client.SetAssistantThreadsStatusContext(ctx, params) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P2] 验收标准与实现不匹配
此行勾选了
CarouselBlock 展示,但实际skills_list.go使用的是DataTableBlock。建议更新为:保持 spec 与代码一致。