mirror of
https://github.com/chenhg5/cc-connect.git
synced 2026-10-25 17:36:43 +08:00
* feat(googlechat): scaffold platform skeleton Add a Google Chat platform adapter registered as "googlechat", wired into selective compilation via plugin_platform_googlechat.go (build tag no_googlechat) and the Makefile ALL_PLATFORMS list. This commit lands the skeleton only: option parsing/validation in New (gws_path, project, target, subscription, allow_from, trigger, session_scope, credentials_file) plus stub Start/Reply/Send/Stop. Receive, send and gating follow in subsequent commits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(googlechat): receive messages via gws events subscribe Start() now supervises a `gws events +subscribe` subprocess: stdout is scanned as NDJSON (one Workspace Events CloudEvent per line, buffer enlarged for the full message resource), stderr is logged, and the process is restarted with a small backoff if it exits while the context is alive. Stop() cancels the context to kill it. parseEvent decodes chat.message.v1.created envelopes, ignores non-human senders (so the app never replies to its own posts), and dispatches a core.Message carrying the space/thread reply context. Trigger-word, allow_from gating and session scoping follow in a later commit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(googlechat): send replies via gws chat messages create Reply and Send post content through `gws chat spaces messages create`, using the generic raw method (--params/--json) rather than the +send helper so replies can be threaded: when the reply context carries a thread name, the request sets messageReplyOption and thread.name so the answer lands in the originating thread. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(googlechat): add trigger-word, allow_from and session scoping parseEvent now gates and routes incoming messages: - trigger word: when configured, only messages starting with the prefix are handled (prefix stripped) for user-OAuth mode without a Chat App; otherwise argumentText (mention markup already removed by Google) is used, falling back to text. - allow_from via core.AllowList, and a core.IsOldMessage guard on createTime so a restart does not replay backlog. - session_scope space|thread|user via buildSessionKey, plus ReconstructReplyCtx so proactive sends reach the right space/thread. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(googlechat): add config example Document the googlechat platform options in config.example.toml, including the gws prerequisites and the two operating modes (@mention with a Chat App service account, vs trigger-word with user OAuth). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(googlechat): add event parsing and reply-arg unit tests Cover parseEvent (mention vs trigger mode, argumentText fallback, non-human and wrong-type skipping, allow_from, old-message guard), buildSessionKey for each scope, ReconstructReplyCtx round-trips, and buildCreateArgs threaded vs top-level argument shapes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(googlechat): switch to Chat app Pub/Sub receive + service-account send Pivot the platform from the Workspace Events API to a registered Google Chat app's native Cloud Pub/Sub connection. The subscription is fixed, so there is no Workspace Events subscription expiry or per-restart resource leak (the two operational problems of the previous approach). - receive: parse the Chat-app event format — gws wraps each Pub/Sub message as {"data":{"type":"MESSAGE","message":{...}}} — instead of Workspace Events CloudEvents; pull a fixed subscription via `gws events +subscribe --subscription`. - send: post via the Chat REST API authenticated as the app's service account (chat.bot scope) so replies appear as the bot. gws has no service-account auth, so sending is native Go via golang.org/x/oauth2/google; receiving still uses gws (its own OAuth). - config: require `subscription`; `credentials_file` is the SA key used for replies. Drop the Workspace Events target/project auto-create. Verified end-to-end against a real Chat app: the receive format matches gws output and a service-account reply posts as the bot. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(googlechat): add GCP/Chat app setup guide Add docs/googlechat.md documenting the end-to-end Google Chat setup: enabling the Chat + Pub/Sub APIs, creating the Pub/Sub topic/subscription and granting the Chat push service account publisher, creating a service account key for bot replies, configuring the Chat app (LIVE, classic model, Cloud Pub/Sub connection, visibility), cc-connect config, and an FAQ covering the issues hit during bring-up (app must be LIVE to send, Workspace-only, add-on vs classic). Link it from the README platform table. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(googlechat): receive via native Pub/Sub client instead of gws Replace the `gws events +subscribe` subprocess with a native streaming pull using cloud.google.com/go/pubsub. Sending was already native Go, so this removes the gws binary dependency entirely — the platform is now fully native. Both directions now authenticate with the same service-account key, so `credentials_file` is required (it also needs roles/pubsub.subscriber on the subscription for the receive path). The `gws_path` option is removed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(googlechat): drop trigger, centralize session-key constants, harden session_scope (#5) - Remove the `trigger` field and its prefix-matching branch from extractContent; Google Chat already scopes delivery so a prefix filter is redundant and subtly broken for @mention spaces (m.Text still contains the markup). - Extract sessionKeyPrefix and threadSep constants used by both buildSessionKey and ReconstructReplyCtx so the encode/decode pair stays in sync automatically. - Add a slog.Warn in the normalizeSessionScope default branch so typos like "thraed" surface at startup instead of silently falling back to "space". - Update tests, docs/googlechat.md, and config.example.toml accordingly. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(googlechat): implement FormattingInstructions for Google Chat text formatting (#7) * feat(googlechat): implement FormattingInstructions for Google Chat text formatting Adds FormattingInstructions() to *Platform so the engine injects Google Chat-specific formatting guidance into the agent system prompt, steering agents away from unsupported ## headings and [text](url) links. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(googlechat): add block quote and display-text link to FormattingInstructions Add missing formatting rules per reviewer feedback: - Block quote syntax (>text) - Display-text hyperlink syntax (<url|text>) alongside raw URL auto-linking Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(googlechat): fix test call after base-branch refactor of newTestPlatform Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(googlechat): implement streaming preview (PreviewStarter + MessageUpdater) (#6) * feat(googlechat): implement streaming preview via PreviewStarter + MessageUpdater Adds SendPreviewStart (POST a threaded message, return resource-name handle) and UpdateMessage (PATCH spaces.messages with updateMask=text) so the engine's real-time streaming-preview path is activated for Google Chat. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(googlechat): extract doRequest helper + drain response bodies Extract a `doRequest` helper that centralizes HTTP execution and non-2xx error handling. Both `post()` and `SendPreviewStart` become thin wrappers, eliminating the duplicated logic that would otherwise diverge on future changes (retries, auth header tweaks, etc.). Drain response bodies on success paths in `post()`, `SendPreviewStart`, and `UpdateMessage` so the transport can return TCP connections to the pool. Addresses the performance issue where each streaming PATCH would open a new TLS+TCP handshake. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(googlechat): send images and files via attachment upload (#8) * feat(googlechat): implement SendImage and SendFile via attachment upload Adds media-upload support to the Google Chat platform so cc-connect can send images and files (e.g. /diff HTML output) instead of returning ErrNotSupported. Two-step flow for each send: 1. Upload raw bytes to the Chat media endpoint (multipart/related POST to https://chat.googleapis.com/upload/v1/{space}/attachments:upload) and retrieve the attachmentDataRef resource name. 2. Create a Chat message that references the data ref, respecting thread context the same way as the existing text send path. Shared helper postAttachment handles both steps; SendImage and SendFile delegate to it with appropriate filename/MIME defaults. Compile-time interface assertions ensure *Platform satisfies core.ImageSender and core.FileSender. Adds TestBuildAttachmentRequest mirroring TestBuildSendRequest to cover the message body shape (attachment data ref + thread, reply option URL). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(googlechat): address code-review feedback on SendImage/SendFile - Add rc.space == "" guard to postAttachment (bug fix: prevents malformed upload URL when ReconstructReplyCtx("googlechat:") yields empty space) - Pre-size bytes.Buffer in uploadAttachment to avoid a full double-copy of attachment data on large files - Extract httpErrorBody helper to deduplicate the 3 error-drain sites - Extract messageURL/applyThread helpers to deduplicate threading URL logic shared between buildSendRequest and buildAttachmentRequest - Extract coalesce helper and simplify SendImage/SendFile - Use doRequest in postAttachment for the create-message step - Add HTTP-level unit tests: uploadAttachment (success, 403 error, empty resourceName), postAttachment (missing space, two-step flow), SendImage/SendFile (default filename/MIME fallbacks) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(googlechat): use doRequest in uploadAttachment and drain response body Two follow-up fixes from code review: - Route upload request through doRequest so it benefits from any future centralized logging/retry/tracing alongside post() and postAttachment(). - Drain remaining bytes from resp.Body after json.Decode so the HTTP transport can return the connection to the pool (partial reads prevent connection reuse, causing a new TLS handshake per SendImage/SendFile call). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(googlechat): drain resp.Body on decode error in uploadAttachment Deferred drain ensures the HTTP connection is returned to the pool even when JSON decoding fails, not just on success. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(googlechat): handle Stop error, remove redundant drain defer, add compile-time assertion (#12) - Return psClient.Close() error from Stop() instead of silently discarding it - Remove redundant drain defer in uploadAttachment; the json.Decoder already consumes the body, and defer resp.Body.Close() handles cleanup - Add compile-time assertion var _ core.ReplyContextReconstructor = (*Platform)(nil) alongside the existing ImageSender and FileSender guards Closes #10 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(googlechat): use doRequest in uploadAttachment and drain response body Two follow-up fixes from code review: - Route upload request through doRequest so it benefits from any future centralized logging/retry/tracing alongside post() and postAttachment(). - Drain remaining bytes from resp.Body after json.Decode so the HTTP transport can return the connection to the pool (partial reads prevent connection reuse, causing a new TLS handshake per SendImage/SendFile call). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(googlechat): drain resp.Body on decode error in uploadAttachment Deferred drain ensures the HTTP connection is returned to the pool even when JSON decoding fails, not just on success. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(googlechat): clear lint issues in googlechat package (#13) * fix(googlechat): resolve package lint errors Handle all body/multipart close errors, remove deprecated auth option usage, and tighten HTTP test writers so golangci-lint passes for platform/googlechat. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(googlechat): ack Pub/Sub message after handler, nack on panic Previously m.Ack() was called before p.handler(), so a handler panic or failure would silently drop the message without redelivery. Move ack to after handler completion and nack on panic so Pub/Sub can redeliver. Introduce the ackable interface and dispatchMessage to make the timing testable without a real Pub/Sub client, and add three regression tests covering the success, panic, and parse-failure paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * test(googlechat): add unit tests for New() error cases (#14) Closes #11 Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
107 lines
4.7 KiB
AMPL
107 lines
4.7 KiB
AMPL
module github.com/chenhg5/cc-connect
|
|
|
|
go 1.25.0
|
|
|
|
require (
|
|
cloud.google.com/go/pubsub/v2 v2.4.0
|
|
github.com/BurntSushi/toml v1.6.0
|
|
github.com/bwmarrin/discordgo v0.29.0
|
|
github.com/charmbracelet/bubbles v1.0.0
|
|
github.com/charmbracelet/bubbletea v1.3.10
|
|
github.com/charmbracelet/lipgloss v1.1.0
|
|
github.com/creack/pty v1.1.24
|
|
github.com/go-telegram/bot v1.20.0
|
|
github.com/google/uuid v1.6.0
|
|
github.com/gorilla/websocket v1.5.0
|
|
github.com/larksuite/oapi-sdk-go/v3 v3.5.3
|
|
github.com/line/line-bot-sdk-go/v8 v8.19.0
|
|
github.com/mdp/qrterminal/v3 v3.2.1
|
|
github.com/open-dingtalk/dingtalk-stream-sdk-go v0.9.1
|
|
github.com/robfig/cron/v3 v3.0.1
|
|
github.com/slack-go/slack v0.16.0
|
|
github.com/stretchr/testify v1.11.1
|
|
golang.org/x/crypto v0.50.0
|
|
golang.org/x/oauth2 v0.36.0
|
|
google.golang.org/api v0.272.0
|
|
maunium.net/go/mautrix v0.27.0
|
|
modernc.org/sqlite v1.49.1
|
|
rsc.io/qr v0.2.0
|
|
)
|
|
|
|
require (
|
|
cloud.google.com/go v0.123.0 // indirect
|
|
cloud.google.com/go/auth v0.18.2 // indirect
|
|
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
|
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
|
cloud.google.com/go/iam v1.5.3 // indirect
|
|
filippo.io/edwards25519 v1.2.0 // indirect
|
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
|
github.com/charmbracelet/colorprofile v0.4.1 // indirect
|
|
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
|
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
|
|
github.com/charmbracelet/x/term v0.2.2 // indirect
|
|
github.com/clipperhouse/displaywidth v0.9.0 // indirect
|
|
github.com/clipperhouse/stringish v0.1.1 // indirect
|
|
github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
|
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
|
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
|
github.com/go-logr/logr v1.4.3 // indirect
|
|
github.com/go-logr/stdr v1.2.2 // indirect
|
|
github.com/gogo/protobuf v1.3.2 // indirect
|
|
github.com/google/s2a-go v0.1.9 // indirect
|
|
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
|
|
github.com/googleapis/gax-go/v2 v2.18.0 // indirect
|
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
github.com/mattn/go-localereader v0.0.1 // indirect
|
|
github.com/mattn/go-runewidth v0.0.19 // indirect
|
|
github.com/mattn/go-sqlite3 v1.14.42 // indirect
|
|
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
|
github.com/muesli/termenv v0.16.0 // indirect
|
|
github.com/ncruces/go-strftime v1.0.0 // indirect
|
|
github.com/petermattis/goid v0.0.0-20260330135022-df67b199bc81 // indirect
|
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
|
github.com/rivo/uniseg v0.4.7 // indirect
|
|
github.com/rs/zerolog v1.35.0 // indirect
|
|
github.com/stretchr/objx v0.5.2 // indirect
|
|
github.com/tidwall/gjson v1.18.0 // indirect
|
|
github.com/tidwall/match v1.1.1 // indirect
|
|
github.com/tidwall/pretty v1.2.1 // indirect
|
|
github.com/tidwall/sjson v1.2.5 // indirect
|
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
|
github.com/yuin/goldmark v1.8.2 // indirect
|
|
go.einride.tech/aip v0.83.0 // indirect
|
|
go.mau.fi/util v0.9.8 // indirect
|
|
go.opencensus.io v0.24.0 // indirect
|
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
|
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
|
go.opentelemetry.io/otel v1.42.0 // indirect
|
|
go.opentelemetry.io/otel/metric v1.42.0 // indirect
|
|
go.opentelemetry.io/otel/sdk v1.42.0 // indirect
|
|
go.opentelemetry.io/otel/sdk/metric v1.42.0 // indirect
|
|
go.opentelemetry.io/otel/trace v1.42.0 // indirect
|
|
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
|
|
golang.org/x/net v0.53.0 // indirect
|
|
golang.org/x/sync v0.20.0 // indirect
|
|
golang.org/x/sys v0.43.0 // indirect
|
|
golang.org/x/term v0.42.0 // indirect
|
|
golang.org/x/text v0.36.0 // indirect
|
|
golang.org/x/time v0.15.0 // indirect
|
|
google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d // indirect
|
|
google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5 // indirect
|
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect
|
|
google.golang.org/grpc v1.79.3 // indirect
|
|
google.golang.org/protobuf v1.36.11 // indirect
|
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
modernc.org/libc v1.72.0 // indirect
|
|
modernc.org/mathutil v1.7.1 // indirect
|
|
modernc.org/memory v1.11.0 // indirect
|
|
)
|