mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-19 08:15:12 +00:00
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
This commit is contained in:
1 parent
7205e6bb3b
commit
fb8afd41bf
44 files changed
+4214
-39
No files matched your search
@@ -64,10 +64,23 @@ func (l *Loop) shouldShareKnowledgeGraph() bool {
|
||||
return l.workspaceSharing != nil && l.workspaceSharing.ShareKnowledgeGraph
|
||||
}
|
||||
|
||||
// buildChannelMeta extracts channel metadata from RunRequest for bootstrap decisions.
|
||||
// Returns nil when channel type is unknown (preserves normal bootstrap flow).
|
||||
func (l *Loop) buildChannelMeta(req *RunRequest) *bootstrap.ChannelMeta {
|
||||
if req == nil || req.ChannelType == "" {
|
||||
return nil
|
||||
}
|
||||
return &bootstrap.ChannelMeta{
|
||||
ChannelType: req.ChannelType,
|
||||
DisplayName: req.SenderName,
|
||||
DefaultTimezone: l.defaultTimezone,
|
||||
}
|
||||
}
|
||||
|
||||
// getOrCreateUserSetup returns the cached userSetup for a user, creating it on first call.
|
||||
// On first call: seeds context files (non-team) and resolves workspace from user profile.
|
||||
// On subsequent calls: returns cached setup immediately (no DB calls).
|
||||
func (l *Loop) getOrCreateUserSetup(ctx context.Context, userID, channel string, isTeamSession bool) *userSetup {
|
||||
func (l *Loop) getOrCreateUserSetup(ctx context.Context, userID, channel string, isTeamSession bool, channelMeta *bootstrap.ChannelMeta) *userSetup {
|
||||
if userID == "" {
|
||||
return &userSetup{workspace: l.workspace}
|
||||
}
|
||||
@@ -92,7 +105,7 @@ func (l *Loop) getOrCreateUserSetup(ctx context.Context, userID, channel string,
|
||||
}
|
||||
// Step 2: Seed context files (must run before buildMessages reads them).
|
||||
// Passes isNew so SeedUserFiles knows whether to skip existing files.
|
||||
if err := l.seedUserFiles(ctx, l.agentUUID, userID, l.agentType, isNew); err != nil {
|
||||
if err := l.seedUserFiles(ctx, l.agentUUID, userID, l.agentType, isNew, channelMeta); err != nil {
|
||||
slog.Warn("failed to seed user context files", "error", err)
|
||||
// Seeding failed (e.g. SQLITE_BUSY after retries). Inject
|
||||
// embedded bootstrap templates in-memory so the first turn
|
||||
|
||||
Reference in new issue
Block a user