Files
goclaw/cmd/gateway_callbacks.go
T
Plateau Nguyen fb8afd41bf feat(channels): add Facebook Messenger and Pancake channel integrations (#731)
Add two new channel implementations for Facebook Fanpage (comment + Messenger
auto-reply, first inbox DM) and Pancake/pages.fm (multi-platform inbox via
Facebook, Zalo, Instagram, TikTok, WhatsApp, LINE).

Key features:
- Facebook: comment auto-reply, Messenger auto-reply, first inbox DM,
  HMAC-SHA256 webhook verification, multi-page webhook routing
- Pancake: multi-platform inbox, outbound echo dedup with HTML normalization,
  race-condition-safe echo fingerprinting, platform-aware formatting
- Bootstrap skip: pre-fill USER.md from channel metadata (Pancake)
- SanitizeDisplayName across all channels (defense in depth)

Code audit fixes:
- Fix truncateForTikTok byte→rune slicing (UTF-8 corruption)
- Fix empty message.ID shared dedup slot (silent message loss)
- Fix DisplayName markdown injection in buildPrefilledUser
- Consolidate duplicate ChannelMeta type (agent→bootstrap)
- Compile-time interface assertions, alphabetical type constants
- Per-message logs demoted to slog.Debug, errors.As for wrapped errors
- UI: alphabetical channel ordering, complete config schemas
- Remove deprecated WhatsApp bridge_url, fix nested error parsing
2026-04-09 23:15:08 +07:00

67 lines
2.7 KiB
Go

package cmd
import (
"context"
"github.com/google/uuid"
"github.com/nextlevelbuilder/goclaw/internal/agent"
"github.com/nextlevelbuilder/goclaw/internal/bootstrap"
"github.com/nextlevelbuilder/goclaw/internal/store"
"github.com/nextlevelbuilder/goclaw/internal/tools"
)
// buildEnsureUserProfile creates the user profile resolution callback.
// Creates/resolves user profile and returns effective workspace.
// Separated from seeding to allow independent lifecycle management.
func buildEnsureUserProfile(as store.AgentStore) agent.EnsureUserProfileFunc {
return func(ctx context.Context, agentID uuid.UUID, userID, workspace, channel string) (string, bool, error) {
isNew, effectiveWs, err := as.GetOrCreateUserProfile(ctx, agentID, userID, workspace, channel)
if err != nil {
return effectiveWs, false, err
}
return effectiveWs, isNew, nil
}
}
// buildSeedUserFiles creates the context file seeding callback.
// Seeds BOOTSTRAP.md, USER.md, etc. into user_context_files.
// isNew=true seeds all files; isNew=false only seeds if user has zero files
// (avoids re-seeding BOOTSTRAP.md after auto-cleanup on server restart).
func buildSeedUserFiles(as store.AgentStore) agent.SeedUserFilesFunc {
return func(ctx context.Context, agentID uuid.UUID, userID, agentType string, isNew bool, channelMeta *bootstrap.ChannelMeta) error {
_, err := bootstrap.SeedUserFiles(ctx, as, agentID, userID, agentType, !isNew, channelMeta)
return err
}
}
// buildBootstrapCleanup creates a callback that removes BOOTSTRAP.md for a user.
// Used as a safety net after enough conversation turns, in case the LLM
// didn't clear BOOTSTRAP.md itself. Idempotent — no-op if already cleared.
func buildBootstrapCleanup(as store.AgentStore) agent.BootstrapCleanupFunc {
return func(ctx context.Context, agentID uuid.UUID, userID string) error {
return as.DeleteUserContextFile(ctx, agentID, userID, bootstrap.BootstrapFile)
}
}
// buildCacheInvalidate creates a callback that invalidates the context file cache
// for a user after SeedUserFiles writes via raw agentStore. Without this,
// LoadContextFiles may return stale (empty) cached results on the first turn.
func buildCacheInvalidate(intc *tools.ContextFileInterceptor) agent.CacheInvalidateFunc {
if intc == nil {
return nil
}
return func(agentID uuid.UUID, userID string) {
intc.InvalidateUser(agentID, userID)
}
}
// buildContextFileLoader creates the per-request context file loader callback.
// Delegates to the ContextFileInterceptor for type-aware routing.
func buildContextFileLoader(intc *tools.ContextFileInterceptor) agent.ContextFileLoaderFunc {
return func(ctx context.Context, agentID uuid.UUID, userID, agentType string) []bootstrap.ContextFile {
return intc.LoadContextFiles(ctx, agentID, userID, agentType)
}
}