# dsh-qqbot

> Connect a QQ bot to DeepSeek Harness: mention @bot in private or group chats to chat, send files, and call tools. Scan QR code to bind QQ credentials, with independent sessions spawned per message.

## Metadata

- Author: [@tencent-connect](https://github.com/tencent-connect)
- Repo: <https://github.com/tencent-connect/dsh-qqbot.git>
- GitHub: [tencent-connect/dsh-qqbot](https://github.com/tencent-connect/dsh-qqbot)
- Stars: 70
- Language: TypeScript
- License: [MIT](https://spdx.org/licenses/MIT.html)
- Topics: `dsh`, `dsh-plugin`, `qqbot`
- Forks: 11
- Open Issues: 9
- Last push: 2026-08-18T15:11:41.000Z
- Added: 2026-08-17T00:00:00.000Z

## Install

```bash
dsh plugin --profile web add github:tencent-connect/dsh-qqbot
```

## Wiki

## One-Sentence Overview

dsh-qqbot connects a QQ bot to DeepSeek Harness: turning the QQ messaging platform into a frontend channel for dsh agents. Just @ the bot in private or group chats to converse, send files, and invoke agent tools. Scan a QR code on first launch to bind QQ credentials; each chat window holds its own independent session.

## Core Capabilities

- **Private & Group Chat**: Private chats always respond; group chats trigger only when @bot by default, with an option to disable the gate and respond to all messages
- **QR Code Credential Binding**: On first launch, the terminal prints a QR code; scan it with QQ mobile to obtain AppID/AppSecret and automatically write to profile
- **Attachment Handling**: Images include size descriptions automatically; voice messages are transcribed to text; file attachments are downloaded locally and the model is prompted to read them via path
- **Markdown Streaming Replies**: Private chats push replies paragraph by paragraph (streaming) by default; group chats send entire paragraphs at once due to QQ protocol limitations; long replies are split by code block/table awareness
- **Independent Sessions**: Each QQ user/group maps to a dsh agent; sessions are persisted via SHA-256 derived sessionIds and can be restored after restart
- **Built-in Slash Commands + Access Control**: `/new` `/compact` `/model` `/stop` plus `/bot-ping` `/bot-version` `/bot-status` `/bot-help`, with openid/groupOpenid whitelist support to allow or disable private and group chats

## Technical Implementation

- **Language**: TypeScript (ESM, `tsconfig` compiles to `dist/`, `main` points to `dist/index.js`)
- **Key Dependencies**: `@tencent-connect/qqbot-nodejs` (QQ OpenAPI + WebSocket client + middleware chain), `@tencent-connect/qqbot-connector` (QR code binding), `js-yaml` (cordis.patch.yml read/write)
- **Architecture Pattern**: Pure Cordis plugin, `inject = ['agents']`; entry `apply()` runs credential guidance then calls `bootstrapGateway()` to assemble SDK middleware chain (error handling → filtering → access control → group history buffer → @ gate → cleaning → rate limiting → slash commands → concurrent serialization → input state → quoting → attachments → envelope), and routes dsh outbound events back to QQ via `ctx.on('session/event', ...)`
- **Entry File**: `src/index.ts` (Cordis plugin entry, includes credential guidance); gateway assembly in `src/gateway/bootstrap.ts`

## Use Cases

Ideal for users who want to interact with DeepSeek Harness via QQ: ask questions in QQ daily, let the agent read files, write code, or run commands—no need to open the web console. Can also serve as a team assistant in QQ groups, with each person's conversation context isolated from others. Resources are automatically released after 30 minutes of inactivity.

## Prerequisites and Compatibility

| Dependency | Minimum Version | Description |
|---|---|---|
| DeepSeek Harness | `0.1.0-rc.6+` | `peerDependencies` locks `@deepseek-ai/dsh-agent` / `dsh-llm` / `dsh-session` / `cordis` / `schemastery` all to 0.1.0-rc.6 / 4.0.1 / 3.18.1 or above |
| QQ Bot Credentials | — | AppID + AppSecret required; can be obtained via QR scan or manually injected via `QQBOT_APPID` / `QQBOT_SECRET` environment variables |
| Node.js | Not declared | No `engines` field in `package.json`; `devDependencies` include `@types/node ^26.2.0`, actual runtime requires Node 18+ supporting built-in APIs like `fetch`, `AbortSignal.timeout`, `node:dns/promises` |
| Platform | macOS / Windows / Linux | QR scan guidance auto-switches to `set` syntax on Windows (`src/setup.ts:186`), other platforms use `export`; no other platform-specific branches in code |
| Native Modules | None | Only depends on built-in modules like `node:crypto` / `node:fs` / `node:path` / `node:dns`, no native bindings like `node-pty` / `sqlite` |

## Installation

```bash
dsh plugin --profile web add github:tencent-connect/dsh-qqbot
```

## Configuration Options

The plugin declares fields via Schemastery Schema (`src/config.ts:56`); the "Description" column in the table below is in plain language. Default values work for normal scenarios after installation; regular users don't need to adjust.

| Config | Type | Description | Default |
|---|---|---|---|
| `appId` | string | QQ bot AppID; if left empty, QR scan guidance runs on startup | empty (requires QR scan or env var) |
| `appSecret` | string | QQ bot AppSecret; same as above | empty |
| `provider` | string | Default LLM provider; if empty, inherits host config | inherits |
| `model` | string | Default model; if empty, inherits host config | inherits |
| `preset` | string | Associated agent preset id (toolset, prompt, etc.) | none |
| `cwd` | string | Root directory for agent command execution | current process directory |
| `requireMention` | boolean | Whether group chats require @bot to respond | `true` |
| `groupPrompt` | string | Additional content injected into system prompt for group chats | none |
| `directPrompt` | string | Additional content injected into system prompt for private chats | none |
| `textChunkLimit` | number | Maximum characters per QQ message (QQ limit ~5000) | `4500` |
| `streaming` | boolean | Whether private chats enable streaming replies (group chats never stream) | `true` |
| `sessionIdleTimeout` | number | Milliseconds before idle sessions are auto-reclaimed | `1800000` (30 minutes) |
| `maxQueue` | number | Maximum messages queued for the same session | `20` |
| `processingTimeoutMs` | number | Milliseconds before a single LLM call auto-terminates | `120000` (2 minutes) |
| `historyLimit` | number | Number of recent messages included as context when responding in group chats | `10` |
| `access.c2cMode` | string | Private chat access mode: `open` allow all / `allowlist` whitelist / `disabled` deny all | `open` |
| `access.c2cAllow` | string array | Private chat whitelist (enter user openid) | `[]` |
| `access.groupMode` | string | Group chat access mode (same values) | `open` |
| `access.groupAllow` | string array | Group chat whitelist (enter group openid) | `[]` |
| `showToolResults` | boolean | Whether to display successful tool call results (errors always shown) | `false` |
| `debug` | boolean | Enable debug logs (includes middleware hit details) | `false` |

## FAQ

**Q: What do I need to do on first launch?**

A: If AppID/AppSecret is not detected on startup, the plugin prints a QR code in the terminal; scan to complete binding. Credentials are automatically written to the current profile's cordis.patch.yml, no need to scan again next time. When QR code is misaligned, use the browser link output by version 0.4.0+ instead.

**Q: Do I need to @bot in group chats for it to respond?**

A: `requireMention` is enabled by default; group chats only trigger responses when @bot. Set to false to respond to any message. Private chats are unaffected by this switch and always respond.

**Q: Will conversation context leak between different QQ groups?**

A: No. Each QQ private chat user and each QQ group has an independent sessionKey. sessionId is deterministically derived via SHA-256 from `qqbot:${appId}:${scope}:${peerId}`, restorable after restart. Dialogues between different groups/people are isolated.

**Q: Does it support images, voice, and files?**

A: Images include size descriptions in context. Voice messages prefer ASR-transcribed text; if the model can't access audio, the original link is attached as fallback. File attachments are automatically downloaded to `${cwd}/.qqbot/${messageId}/` with path prompts, then the model reads them via tools. File downloads include HTTPS + SSRF protection and 20MB size limits.

**Q: How to enable streaming replies?**

A: Streaming is on by default for private chats (`streaming: true`), replies push paragraph by paragraph. Group chats never stream due to QQ group message API limitations, only sending entire Markdown blocks. If global streaming is disabled, private chats also switch to sending complete replies at once.

**Q: How to switch models?**

A: Send `/model` in chat to list available models (click to switch), or `/model provider/model` to specify a route. Preferences are persisted per sessionKey to `~/.dsh-qqbot/model-prefs.json`, so the same person/group retains settings on next restart.

**Q: How to completely reset the current conversation?**

A: Send `/new` (alias `/reset`, `/clear`) to discard the current session and start a new one. To preserve context but compress history, send `/compact` (requires host with compaction service). Long idle sessions over 30 minutes are auto-disposed and restored on next message.

**Q: "Credentials not configured" error—now what?**

A: QR scanning automatically writes to profile. In dev mode (loading source via `cordis.dev.yml`), the profile directory can't be located, so the plugin prints export/set environment variable instructions to terminal. For manual configuration, write `appId` and `appSecret` to the corresponding plugin entry in the profile's cordis.patch.yml.

## Learning Curve

Beginner — scan QR code to bind QQ credentials, start chatting in private messages immediately. Advanced users can adjust group gating, whitelists, Markdown split thresholds, and other parameters as needed.

## Known Issues and Limitations

- Model preferences are persisted per sessionKey to `~/.dsh-qqbot/model-prefs.json`; deleting this file resets all preferences. No UI-based batch cleanup currently available.
- Group chat streaming is always disabled: QQ group message API doesn't support incremental updates, so group chats only send entire Markdown blocks, with single-message limits controlled by `textChunkLimit` (default 4500).
- File attachment downloads have 20MB size limits and HTTPS + SSRF protection. When either rule is triggered, download is skipped and only path description is retained; oversized files need to be manually placed in the agent's working directory.
- Sessions idle for 30 minutes are automatically disposed; the next message triggers a "resume → create" flow. If the host lacks `sessions` service, resume fails and falls back to creating a brand new session (history lost).
- Model switching uses fork + rebuild strategy. If `sessions.fork` service doesn't exist at the lower level, it falls back to disposing old session and creating new one with new model (equivalent to `/new`, context lost).
- `/compact` command requires host to have `agent-presets` or `compaction` service loaded; returns "compression capability unavailable" when not loaded.
- In streaming replies, both `assistant/chunk` and `assistant/message` enter the buffer. If `turn/end` ends abnormally mid-stream,底层错误码 like `STREAM_CLOSED` are silently ignored (won't appear in user messages).

---

This document is auto-generated by [deepseek-plugin.org](https://deepseek-plugin.org). HTML page: [dsh-qqbot](https://deepseek-plugin.org/plugins/tencent-connect/dsh-qqbot)
Wiki generated by AI (model: `MiniMax-M2.7`)
