Skip to content

refactor(notification): channel notification bots with pull delivery and web UI#91

Merged
RussellLuo merged 5 commits into
OpenCSGs:mainfrom
wanghaojie124:dev
May 21, 2026
Merged

refactor(notification): channel notification bots with pull delivery and web UI#91
RussellLuo merged 5 commits into
OpenCSGs:mainfrom
wanghaojie124:dev

Conversation

@wanghaojie124
Copy link
Copy Markdown
Collaborator

Move notifier delivery from runtime agents to type=notification channel bots, with relay pull/push, dedicated API routes, and supervisor fixes that read stored tokens for pull eligibility. Web UI adds notification sidebar section, simplified notifier profile controls, and CLI bot list limited to normal bots.

wanghj added 3 commits May 20, 2026 18:36
…and web UI

Move notifier delivery from runtime agents to type=notification channel bots,
with relay pull/push, dedicated API routes, and supervisor fixes that read
stored tokens for pull eligibility. Web UI adds notification sidebar section,
simplified notifier profile controls, and CLI bot list limited to normal bots.
Use n-{name} ids for notification bots to avoid worker/IM collisions,
guard delete when user_id is shared, restrict bot GET/PATCH to notification
type, and log empty pull polls at debug.
Pass onAgentCreateBotKindChange to AgentProfileModal so Worker/notification
tabs update state after workspace controller refactor.
Comment thread internal/bot/service.go Outdated

filtered := make([]Bot, 0, len(all))
for _, b := range all {
if IsNotificationBot(b) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unconditional skip means the normal list API now hides notification bots for every channel. For the CLI behavior we want to avoid adding a separate bot notification list command: bot list --channel csgclaw should include both type=normal and type=notification, while bot list --channel feishu should continue to return only normal bots. Please move that channel-specific filtering into this list path and drop the separate notification-list surface/tests.

Comment thread internal/api/router.go Outdated
r.Get("/", h.handleBotByID)
r.Patch("/", h.handleBotByID)
r.Delete("/", h.deleteBot)
r.Post("/notifications", h.pushNotificationBot)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Route structure suggestion: keep common bot CRUD on the generic /api/v1/channels/{channel}/bots and /api/v1/channels/{channel}/bots/{id} routes, and keep channel-specific actions on explicit channel routes only. In practice that means generic routes handle GET/POST /bots and GET/PATCH/DELETE /bots/{id}, /api/v1/channels/csgclaw/bots/{id}/notifications stays csgclaw-only, and /api/v1/channels/feishu/bots/{id}/events stays feishu-only. This avoids duplicating the csgclaw/feishu CRUD routes while also avoiding a generic /{channel}/bots/{id}/notifications endpoint for channels that do not support notifications.

Comment thread internal/bot/model_type_test.go Outdated

import "testing"

func TestNormalizeBotTypeDefaultsToNormal(t *testing.T) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests are small and cover model.go behavior directly; consider moving them into internal/bot/model_test.go instead of keeping a separate file just for bot type.

Comment thread internal/api/handler.go Outdated
switch r.Method {
case http.MethodGet:
if r.URL.Query().Get("type") == bot.BotTypeNotification {
bots, err := h.botSvc.ListNotificationBots(channelName)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer keeping this type-specific filtering inside internal/bot: the handler can pass type=notification as list criteria, and the service can decide whether to return normal, notification, or both for each channel.

Comment thread cli/bot/bot.go Outdated
if len(fs.Args()) != 0 {
return fmt.Errorf("bot notification list does not accept positional arguments")
}
bots, err := run.APIClient(globals).ListNotificationBots(ctx, *channelName)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably go through the same bot list path with a type filter instead of a notification-specific list API; that keeps CLI/API behavior aligned and avoids a separate notification list surface.


import "testing"

func TestAdvertiseBaseURLForClient(t *testing.T) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a small config helper test; consider moving it into internal/config/config_test.go instead of adding a separate test file for advertise_base_url.

Comment thread internal/config/config.go Outdated
}

// AdvertiseBaseURLForClient returns the base URL exposed to Web UI and external webhook callers.
func AdvertiseBaseURLForClient(server ServerConfig) string {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider renaming this to ResolveAdvertiseBaseURL: the helper does more than return a client value, since it resolves server.advertise_base_url with a fallback derived from listen_addr.

Move notification bot listing into bot.List with channel-aware type
filtering; drop separate notification list CLI/API surfaces and add
bot list --type. Refactor routes so generic CRUD uses /{channel}/bots
while csgclaw push and feishu events stay channel-specific. Rename
AdvertiseBaseURLForClient to ResolveAdvertiseBaseURL and merge small
tests into model_test.go and config_test.go.
Comment thread internal/api/handler.go Outdated
Role: item.Role,
RuntimeKind: item.RuntimeKind,
Image: item.Image,
Env: item.Env,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This added Env assignment does not compile at the current PR head: apitypes.HubTemplate has no Env field, and hub.Template has no Env field either. go test ./cli ./internal/api ./internal/bot ./internal/apiclient fails before the API and CLI packages can build. Please either add the field to both template types or remove this assignment.

Comment thread internal/bot/service.go

filtered := make([]Bot, 0, len(all))
for _, b := range all {
if !shouldIncludeBotInList(b, normalizedChannel, botType) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The store-backed bots are filtered by botType, but configured Feishu bots are appended afterward without seeing that filter. As a result, /api/v1/channels/feishu/bots?type=notification can still return configured Feishu bots, even though those are normal bots and notification bots are meant to be excluded for Feishu. Consider skipping this append when botType normalizes to notification, or pass the type filter into appendConfiguredFeishuBots.

Drop hub template Env from presentHubTemplate on the notifier branch so
API builds without hub types. Skip appendConfiguredFeishuBots when
type=notification so configured Feishu apps are not returned for that
filter.
@RussellLuo
Copy link
Copy Markdown
Collaborator

LGTM

@RussellLuo RussellLuo merged commit b58c8a1 into OpenCSGs:main May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants