Skip to content

Feature/chat pending message and logpanel & feature/multiple-wxwork-customer-service-support - #46

Open
ExtremelyTroublesome wants to merge 17 commits into
huabeitech:devfrom
ExtremelyTroublesome:dev
Open

ExtremelyTroublesome wants to merge 17 commits into
huabeitech:devfrom
ExtremelyTroublesome:dev

Conversation

@ExtremelyTroublesome

Copy link
Copy Markdown

1.增加了AI客服占位消息功能,先回复用户,让其知道后台有接收到信息,等AI处理完毕再回复实际的消息;
2.增加了系统日志功能,让前端也可以查阅到系统关键信息;
3.会话面板增加来源渠道;
4.Web渠道挂件弹窗标题栏增加独立关闭按钮(实际为最小化)
5.重构企业微信客服账号接入及回复方式,支持多个客服及各自接待AIAgent处理会话。

JOY and others added 17 commits September 2, 2026 15:35
- Update TypeScript from ^5.8.3 to ^6.0.3 in web and flowgram-editor
- Ensure compatibility in build-sdk.mjs by safely handling importsNotUsedAsValues
- Update pnpm-lock.yaml in web and flowgram-editor
chore(deps): upgrade TypeScript to v6.0.3
The docs submodule points to huabeitech/agent-desk-docs, which is not publicly accessible (GitHub returns 404). Every fork or fresh clone that runs \`git submodule update --init\` fails on it:

    fatal: repository 'https://github.com/huabeitech/agent-desk-docs.git/' not found

Remove the gitlink and its .gitmodules entry. No source code is affected; the qdrant submodule (public) is kept as-is. An alternative fix is to make the docs repository public.
Set the bootstrap account type explicitly so dashboard authentication accepts it. Add an idempotent migration for existing active bootstrap super admins and cover the repair boundaries in CI.
- Go: grpc 1.83.1 (critical+high), websocket 1.5.3, excelize 2.11.0, quic-go 0.59.1, gomarkdown 759bbc3e3207

- Frontend: next 16.2.11, eslint-config-next 16.2.11, markdown-it 14.2.0, @tiptap 3.30.4, plus a range-scoped pnpm.overrides block pinning flagged transitive packages (fast-uri, nanoid, js-yaml, brace-expansion, postcss, browserslist, ws, undici, sharp, hono, path-to-regexp, picomatch, lodash, langsmith, uuid, linkify-it, immutable, esbuild, flatted, qs, humanfs, ip-address, body-parser, postcss-selector-parser, babel/core) to their patched versions
…and env aliasing

- user_service: ResetPassword and AssignRoles can no longer touch super_admin targets or grant IsSystem roles unless the operator is a super_admin (was: any admin could escalate to super_admin in one request)

- role_service: AssignPermissions refuses to rewrite built-in IsSystem roles (AssignRoles guard) unless the operator is a super_admin

- ws_service: canSubscribeConversation now requires the same conversation.view permission the REST conversation endpoints gate with, closing a websocket read bypass of full message content

- channel_message_outbox: ListPending only returns rows whose backoff elapsed (next_retry_at) ordered by next_retry_at, so a backlog of not-yet-due retries no longer starves newer pending sends

- config: AGENT_DESK_* prefixed aliases are bound first so ambient legacy variables (PORT, DATABASE_URL) cannot silently override the documented configuration
chore: remove docs submodule pointing to a private repository
…bootstrap-admin-user-type

fix(auth): initialize bootstrap admin as employee
chore(deps): upgrade flagged dependencies to patched versions
…ning

fix(security): harden privilege boundaries, ws auth and outbox ordering
…cution

Uploaded assets are served from the application's own origin, so the extension
stored in the key decides how a browser interprets the bytes. Nothing validated
it: /api/message/upload_attachment accepted any file from an authenticated guest
and the storage layer wrote whatever extension the client named, so a planted
.html file ran against the dashboard origin the moment a staff member opened the
attachment link.

- Reject browser-active extensions and payloads (HTML, SVG, XML/XSL, scripts,
  legacy server-side pages) in AssetService.UploadFile and UploadBytes, and
  sniff the leading bytes instead of trusting the client-declared Content-Type
  so renaming a page to .png does not smuggle it through
- Reduce client filenames to a bare basename within the 255 character column,
  cut on a rune boundary, and strip control characters
- Derive the stored extension from an explicit MIME table.
  mime.ExtensionsByType consults the Windows registry, where text/html resolved
  to .ehtml and escaped the block list, and it differs between dev machines and
  production
- Fall back to an inert .bin when no safe extension can be determined, so the
  server never sniffs the payload to decide its own Content-Type
- Serve local assets with X-Content-Type-Options nosniff and force a download
  for anything that is not previewable media, which also neutralises document
  types already sitting in an existing storage root
- Add error.e0348 to both backend locales for the rejection
Adds Discord as a sixth channel type, following the same shape as the Telegram
and Zalo OA integrations: a client package, an inbound webhook service, an
outbound service drained through the channel message outbox, a third-party
handler, and the dashboard form fields.

Inbound

  POST /api/third/discord/webhook[/:channel_id]

Accepts both a bare interaction/webhook body and one nested under "message".
Author, message id, channel id, guild id, attachments and embeds are all read
from either level, so the same endpoint works with the payload shapes Discord and
common bridges emit. Bot authors and empty messages are dropped. An attachment
becomes a readable "[filename] url" line and an embed falls back to its
description or title, so a customer who sends only an image or a link still
produces a message an agent can act on.

Messages map onto the existing conversation model through ExternalSourceDiscord,
so a Discord user gets the same identity resolution, assignment, handoff and
ticket behaviour as every other channel.

Outbound

Agent and AI replies are queued by EnqueueDiscordMessage and drained by cron
every five seconds, with the same immediate-dispatch goroutine, backoff and
retry ceiling as the other channels. Text and HTML go out as content; an image
message is sent as an embed with the asset's signed URL; an attachment appends
its signed URL to the text. The reply target is the discord_channel_id recorded
on the last inbound message, falling back to a DM channel created from the
customer's Discord user id, so a conversation that started in a server stays in
that server.

Guild scoping

GuildID and ChannelScope are enforced on the way in. A bot invited to several
servers answers only in the guild the channel names, and a channel set to
dm_only ignores guild messages. Without this the two fields are stored
configuration that silently does nothing, and an operator has no way to tell why
a bot is answering somewhere it should not.

Credentials

A channel may carry its own bot token. When it does not, the deployment-wide
token is used, resolved from discord.botToken in config.yaml or DISCORD_BOT_TOKEN
in the environment. New DiscordConfig section plus AGENT_DESK_DISCORD_* and
DISCORD_* bindings, documented in .env.example.

Security

The webhook secret is compared with crypto/subtle.ConstantTimeCompare. A
byte-wise != leaks how much of the prefix matched through response timing, which
matters here because the endpoint is unauthenticated by design. Verification is
skipped only when no secret is configured on the channel, matching how the
Telegram integration treats its webhook secret.

Tests

  internal/discord/client_test.go                  client against an httptest stub
  internal/handlers/third/discord_handler_test.go  handler routing and response
  internal/services/discord_inbound_service_test.go
      inbound to conversation to outbox, guild scope matrix (matching guild,
      other guild, DM under a guild-scoped channel, DM and guild message under
      dm_only, unscoped channel), and rejection of a wrong webhook secret
  internal/services/discord_integration_test.go    full round trip

Not included

Discord's interactions endpoint signs requests with an Ed25519 signature over the
raw body, verified against the application public key. This integration ingests
through a shared-secret webhook instead, so the public key field is stored but
not yet used to verify anything. Adding Ed25519 verification, and the OAuth flow
that provisions a bot without a pasted token, are both worthwhile follow-ups.
fix(storage): stop guest uploads from becoming same-origin script execution
feat(channel): add native Discord channel integration
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.

4 participants