Skip to content

feat(sdk/integrations/discord): add Gateway ChatBridge + adapter #60

@alekspetrov

Description

@alekspetrov

Context

Second half of the Discord chat connector (the LAST of 10 connectors) —
depends on the client/data issue. Implement the Gateway WS transport + the
core.ChatBridge/ChatCapable adapter. Source from
/tmp/pilot-src/internal/adapters/discord/ (transport.go, handler.go).
Mirror sdk/integrations/slack/ (bridge.go + adapter.go + the WS client) and the
chat contract (.agent/system/chat-bridge-design.md).

The Gateway transport uses github.com/gorilla/websocketalready a dependency
(added by slack), so go.mod/go.sum should NOT change and go mod tidy stays
a no-op. sdk/{core,log,util,testutil} stay stdlib-only.

Implementation

Add to sdk/integrations/discord/: transport.go (Gateway WS client),
bridge.go (core.ChatBridge), adapter.go (core.ChatCapable), + tests.

  • Remove every github.com/qf-studio/pilot/... import; inject *slog.Logger.
  • transport.go — Gateway WS client: connect, IDENTIFY/RESUME, heartbeat loop,
    reconnect, read GatewayEvents. Uses gorilla/websocket.
  • adapter.go: Adapter with Name() "discord" + NewChatBridge(core.ChatDeps) core.ChatBridge. Compile-time: var _ core.Adapter = (*Adapter)(nil),
    var _ core.ChatCapable = (*Adapter)(nil), var _ core.ChatBridge = (*bridge)(nil).
  • bridge.go implements core.ChatBridge:
    • Start(ctx) — run the Gateway loop; map events to core.MessageEvent:
      MESSAGE_CREATEAction:"message" (content with bot-mention stripped,
      sanitized in the loop before the handler — CRITICAL, feat(sdk/integrations/github): port client, types, converter, notifier, webhook #28-class); leading
      /Action:"command" (Command+Args, NO execution); INTERACTION_CREATE
      (component) → Action:"callback" (CallbackID = interaction id/token, Data =
      custom_id). Enforce AllowedGuilds/AllowedChannels. ChannelID = channel id.
    • Send(ctx, OutboundMessage) (MessageRef, error)SendMessage, or
      SendMessageWithComponents rendering Buttons as an action row.
    • Edit(ctx, ref, text)EditMessage.
    • Ack(ctx, callbackID)CreateInteractionResponse (deferred/ack).
  • Tests use sdk/testutil fakes + an httptest/fake-WS seam (no network);
    include an invisible-Unicode sanitize guard on inbound Text.

⛔ Touch ONLY sdk/integrations/discord/. Do not modify sdk/core/, other
connectors, or (expectedly) go.mod/go.sum.

Acceptance

  • transport.go, bridge.go, adapter.go (+ tests) exist; 3 compile-time core assertions; Name() returns "discord".
  • Start maps message/command/callback to core.MessageEvent; inbound Text sanitized in the live loop before HandleMessage (grep shows the call-site).
  • go mod tidy a no-op (gorilla/websocket already present; no NEW dep); sdk/{core,log,util,testutil} stdlib-only.
  • grep -rn "qf-studio/pilot" sdk/ nothing; no command EXECUTION; Send renders Buttons as an action row.
  • go build ./..., go vet ./..., go test -race ./sdk/integrations/discord/..., gofmt -l sdk/ all clean.

Metadata

Metadata

Assignees

No one assigned

    Labels

    no-decomposeSkip epic detection and decompositionpilotPilot AI will work on thispilot-donePilot completed this

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions