Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ services/webui/config.json
!services/webui/start_webui.sh
!services/webui/override.env

# qq-ai-bot
services/qq-ai-bot/data/


# Open WebUI (new, in root)
webui/*
webui/config.json
Expand Down
8 changes: 8 additions & 0 deletions app/src/serviceMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1113,4 +1113,12 @@ export const serviceMetadata: Record<string, Partial<HarborService>> = {
wikiUrl: `${wikiUrl}/2.3.92-Satellite-Daytona`,
tooltip: 'Self-hosted sandbox platform for AI agents with Docker-in-Docker, computer use, and GPU support.',
},
'qq-ai-bot': {
name: 'qq-ai-bot',
tags: [HST.satellite, HST.api, HST.tools],
projectUrl: 'https://github.com/happysnaker/qq-ai-bot',
logo: 'https://github.com/happysnaker.png?size=200',
wikiUrl: `${wikiUrl}/2.3.93-Satellite-qq-ai-bot`,
tooltip: 'Self-hosted QQ ↔ AI bot scaffold for OneBot 11 / NapCat / LLOneBot with ACP-compatible agents, persistent sessions, progress streaming, and a Docker demo.',
},
};
147 changes: 147 additions & 0 deletions docs/2.3.93-Satellite-qq-ai-bot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
### [qq-ai-bot](https://github.com/happysnaker/qq-ai-bot)

> Handle: `qq-ai-bot`<br/>
> Status API: [http://localhost:35020/readyz](http://localhost:35020/readyz)<br/>
> Reverse WebSocket: `ws://localhost:35021/onebot/v11/ws`

qq-ai-bot is a self-hosted QQ ↔ AI bridge for OneBot 11 adapters such as NapCat and LLOneBot. It focuses on the operational glue between QQ messaging and ACP-compatible agents: persistent sessions, progress streaming, group policies, command handling, and a minimal Docker demo that is easy to wire into an existing self-hosted stack.

This Harbor service is useful when you already have a QQ / OneBot adapter and want a cleaner AI-facing bridge instead of building session management, progress updates, and QQ command routing from scratch.

## Starting

```bash
harbor pull qq-ai-bot
harbor up qq-ai-bot
```

By default Harbor starts `qq-ai-bot` with the upstream Docker image and the built-in mock ACP agent command:

```bash
ACP_AGENT_COMMAND=node
ACP_AGENT_ARGS_JSON=["dist/examples/mock-acp-agent.js"]
ACP_AGENT_WORKDIR=/app
```

That means the service can boot and respond on its admin endpoints immediately, even before you wire it to a real OneBot adapter.

Useful first checks:

```bash
curl http://localhost:35020/healthz
curl http://localhost:35020/readyz
curl http://localhost:35020/metrics
```

Then point your QQ / OneBot implementation at Harbor's reverse WebSocket endpoint:

```text
ws://localhost:35021/onebot/v11/ws
```

If you use an access token, keep the token consistent between the adapter and `HARBOR_QQ_AI_BOT_ONEBOT_ACCESS_TOKEN`.

## Configuration

### Environment Variables

Following options can be set via [`harbor config`](./3.-Harbor-CLI-Reference.md#harbor-config):

```bash
HARBOR_QQ_AI_BOT_HOST_PORT # Host port for the HTTP admin/status endpoints
HARBOR_QQ_AI_BOT_REVERSE_WS_HOST_PORT # Host port for the OneBot reverse WebSocket listener
HARBOR_QQ_AI_BOT_IMAGE # Docker image
HARBOR_QQ_AI_BOT_VERSION # Docker image tag
HARBOR_QQ_AI_BOT_WORKSPACE # Persistent Harbor workspace
HARBOR_QQ_AI_BOT_OPEN_URL # URL opened by `harbor open qq-ai-bot`
HARBOR_QQ_AI_BOT_SESSION_STORE # file or redis
HARBOR_QQ_AI_BOT_SESSION_TTL_MINUTES # Session TTL in minutes
HARBOR_QQ_AI_BOT_REDIS_URL # Redis connection string when session store is redis
HARBOR_QQ_AI_BOT_REDIS_KEY_PREFIX # Redis key prefix
HARBOR_QQ_AI_BOT_ONEBOT_ACCESS_TOKEN # Shared token for the OneBot reverse WebSocket
HARBOR_QQ_AI_BOT_ALLOW_GROUP # Enable group messages
HARBOR_QQ_AI_BOT_REQUIRE_MENTION_IN_GROUP # Require @ mention in groups
HARBOR_QQ_AI_BOT_ALLOW_PRIVATE # Enable private chats
HARBOR_QQ_AI_BOT_ALLOW_GROUP_COMMANDS_WITHOUT_MENTION
HARBOR_QQ_AI_BOT_COMMAND_PREFIX # QQ command prefix, default /
HARBOR_QQ_AI_BOT_PROGRESS_MODE # off or message
HARBOR_QQ_AI_BOT_OUTBOUND_MAX_TEXT_LENGTH # Max text length for outbound QQ messages
HARBOR_QQ_AI_BOT_INBOUND_DEDUPE_WINDOW_MS # Inbound dedupe window
HARBOR_QQ_AI_BOT_INBOUND_DEDUPE_MAX_ENTRIES
HARBOR_QQ_AI_BOT_ACP_AGENT_COMMAND # Command used to start the ACP-compatible agent
HARBOR_QQ_AI_BOT_ACP_AGENT_ARGS_JSON # JSON string array of ACP agent args
HARBOR_QQ_AI_BOT_ACP_AGENT_WORKDIR # ACP agent working directory
HARBOR_QQ_AI_BOT_ACP_REUSE_SESSION # Reuse remote ACP sessions
HARBOR_QQ_AI_BOT_DEFAULT_SYSTEM_PROMPT # Default system prompt when no group override is set
HARBOR_QQ_AI_BOT_ACP_VERBOSE_MODE # normal / verbose / debug
HARBOR_QQ_AI_BOT_ACP_PERMISSION_STRATEGY # allow_once / allow_always / cancel
HARBOR_QQ_AI_BOT_ACP_PROGRESS_THROTTLE_MS # Progress throttling interval
HARBOR_QQ_AI_BOT_ACP_MAX_PROGRESS_UPDATES # Max progress updates per interaction
HARBOR_QQ_AI_BOT_ACP_MAX_INBOUND_IMAGES # Max inbound images per interaction
HARBOR_QQ_AI_BOT_ACP_MAX_INBOUND_IMAGE_BYTES
```

### Service-native Environment

Upstream-native env vars can be written through `harbor env qq-ai-bot` or by editing `services/qq-ai-bot/override.env` directly.

Examples:

```bash
harbor env qq-ai-bot ACP_AGENT_COMMAND traecli
harbor env qq-ai-bot ACP_AGENT_ARGS_JSON '["acp","serve"]'
harbor env qq-ai-bot ACP_AGENT_WORKDIR /workspace
```

This is the easiest way to replace the built-in mock ACP agent with your real local agent runtime.

### Volumes

| Mount | Description |
|-------|-------------|
| `${HARBOR_QQ_AI_BOT_WORKSPACE}/data:/app/data` | Persistent session storage and local runtime data |
| `./services/qq-ai-bot/group-rules.json:/app/config/group-rules.json:ro` | Harbor-managed default group policy file |

The default group policy file gives you a concrete example of per-group prompts and mention rules. Edit it if you want Harbor's default QQ behavior to match your own deployment assumptions.

## Typical deployment shape

A practical self-hosted path looks like this:

1. run `qq-ai-bot` in Harbor
2. run your QQ adapter separately (for example NapCat or LLOneBot)
3. point the adapter's reverse WebSocket target at `ws://host:35021/onebot/v11/ws`
4. point `qq-ai-bot` at your ACP-compatible agent runtime

The Harbor service intentionally does **not** bundle a QQ adapter. That keeps the service focused on the AI bridge layer instead of trying to hide NapCat / LLOneBot login or device-state concerns inside Harbor.

## Operational endpoints

qq-ai-bot exposes three especially useful admin endpoints:

- `/healthz` — lightweight liveness check
- `/readyz` — structured runtime status, build info, session store mode, and wiring state
- `/metrics` — Prometheus-style runtime counters

These make the service more production-friendly than a thin message forwarder and are the main reason it fits Harbor as a reusable satellite service.

## Troubleshooting

```bash
harbor logs qq-ai-bot
```

Common checks:

- `curl http://localhost:35020/readyz` to confirm the service is up
- confirm your OneBot reverse WebSocket target is `ws://localhost:35021/onebot/v11/ws`
- if group chat replies seem missing, verify `HARBOR_QQ_AI_BOT_REQUIRE_MENTION_IN_GROUP`
- if ACP calls fail, inspect `ACP_AGENT_COMMAND`, `ACP_AGENT_ARGS_JSON`, and `ACP_AGENT_WORKDIR`
- if you switch to Redis sessions, verify `HARBOR_QQ_AI_BOT_REDIS_URL` and `HARBOR_QQ_AI_BOT_SESSION_STORE=redis`

## Links

- [Project page](https://happysnaker.github.io/qq-ai-bot/)
- [GitHub Repository](https://github.com/happysnaker/qq-ai-bot)
- [Docker quickstart](https://github.com/happysnaker/qq-ai-bot/blob/main/docs/docker-quickstart.md)
- [Configuration reference](https://github.com/happysnaker/qq-ai-bot/blob/main/docs/configuration.md)
33 changes: 33 additions & 0 deletions profiles/default.env
Original file line number Diff line number Diff line change
Expand Up @@ -1739,3 +1739,36 @@ HARBOR_DAYTONA_SSH_GATEWAY_PRIVATE_KEY=""
HARBOR_DAYTONA_SSH_HOST_KEY=""

HARBOR_DAYTONA_WORKSPACE="./services/daytona/data"

# ----------------------------------------------------------------
# qq-ai-bot
# https://github.com/happysnaker/qq-ai-bot
# ----------------------------------------------------------------
HARBOR_QQ_AI_BOT_HOST_PORT=35020
HARBOR_QQ_AI_BOT_REVERSE_WS_HOST_PORT=35021
HARBOR_QQ_AI_BOT_IMAGE="ghcr.io/happysnaker/qq-ai-bot"
HARBOR_QQ_AI_BOT_VERSION="latest"
HARBOR_QQ_AI_BOT_WORKSPACE="./services/qq-ai-bot"
HARBOR_QQ_AI_BOT_OPEN_URL="http://localhost:35020/readyz"
HARBOR_QQ_AI_BOT_SESSION_STORE="file"
HARBOR_QQ_AI_BOT_SESSION_TTL_MINUTES=120
HARBOR_QQ_AI_BOT_REDIS_URL=""
HARBOR_QQ_AI_BOT_REDIS_KEY_PREFIX="qq-ai-bot"
HARBOR_QQ_AI_BOT_ONEBOT_ACCESS_TOKEN=""
HARBOR_QQ_AI_BOT_ALLOW_GROUP=true
HARBOR_QQ_AI_BOT_REQUIRE_MENTION_IN_GROUP=true
HARBOR_QQ_AI_BOT_ALLOW_PRIVATE=true
HARBOR_QQ_AI_BOT_ALLOW_GROUP_COMMANDS_WITHOUT_MENTION=false
HARBOR_QQ_AI_BOT_COMMAND_PREFIX="/"
HARBOR_QQ_AI_BOT_PROGRESS_MODE="message"
HARBOR_QQ_AI_BOT_OUTBOUND_MAX_TEXT_LENGTH=1400
HARBOR_QQ_AI_BOT_INBOUND_DEDUPE_WINDOW_MS=120000
HARBOR_QQ_AI_BOT_INBOUND_DEDUPE_MAX_ENTRIES=2048
HARBOR_QQ_AI_BOT_ACP_REUSE_SESSION=true
HARBOR_QQ_AI_BOT_DEFAULT_SYSTEM_PROMPT=""
HARBOR_QQ_AI_BOT_ACP_VERBOSE_MODE="verbose"
HARBOR_QQ_AI_BOT_ACP_PERMISSION_STRATEGY="allow_once"
HARBOR_QQ_AI_BOT_ACP_PROGRESS_THROTTLE_MS=800
HARBOR_QQ_AI_BOT_ACP_MAX_PROGRESS_UPDATES=6
HARBOR_QQ_AI_BOT_ACP_MAX_INBOUND_IMAGES=3
HARBOR_QQ_AI_BOT_ACP_MAX_INBOUND_IMAGE_BYTES=6291456
61 changes: 61 additions & 0 deletions services/compose.qq-ai-bot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
services:
qq-ai-bot:
container_name: ${HARBOR_CONTAINER_PREFIX}.qq-ai-bot
image: ${HARBOR_QQ_AI_BOT_IMAGE}:${HARBOR_QQ_AI_BOT_VERSION}
env_file:
- ./.env
- ./services/qq-ai-bot/override.env
environment:
BOT_HOST: 0.0.0.0
BOT_PORT: 8080
DATA_DIR: /app/data
SESSION_STORE: ${HARBOR_QQ_AI_BOT_SESSION_STORE}
SESSION_FILE_PATH: /app/data/sessions.json
SESSION_TTL_MINUTES: ${HARBOR_QQ_AI_BOT_SESSION_TTL_MINUTES}
REDIS_URL: ${HARBOR_QQ_AI_BOT_REDIS_URL}
REDIS_KEY_PREFIX: ${HARBOR_QQ_AI_BOT_REDIS_KEY_PREFIX}
APP_BUILD_REF: harbor
ONEBOT_MODE: reverse
ONEBOT_ACCESS_TOKEN: ${HARBOR_QQ_AI_BOT_ONEBOT_ACCESS_TOKEN}
ONEBOT_REVERSE_WS_HOST: 0.0.0.0
ONEBOT_REVERSE_WS_PORT: 16700
ONEBOT_REVERSE_WS_PATH: /onebot/v11/ws
ONEBOT_ALLOW_GROUP: ${HARBOR_QQ_AI_BOT_ALLOW_GROUP}
ONEBOT_REQUIRE_MENTION_IN_GROUP: ${HARBOR_QQ_AI_BOT_REQUIRE_MENTION_IN_GROUP}
ONEBOT_ALLOW_PRIVATE: ${HARBOR_QQ_AI_BOT_ALLOW_PRIVATE}
ONEBOT_ALLOW_GROUP_COMMANDS_WITHOUT_MENTION: ${HARBOR_QQ_AI_BOT_ALLOW_GROUP_COMMANDS_WITHOUT_MENTION}
ONEBOT_COMMAND_PREFIX: ${HARBOR_QQ_AI_BOT_COMMAND_PREFIX}
ONEBOT_GROUP_CONFIG_FILE: /app/config/group-rules.json
ONEBOT_PROGRESS_MODE: ${HARBOR_QQ_AI_BOT_PROGRESS_MODE}
ONEBOT_OUTBOUND_MAX_TEXT_LENGTH: ${HARBOR_QQ_AI_BOT_OUTBOUND_MAX_TEXT_LENGTH}
ONEBOT_INBOUND_DEDUPE_WINDOW_MS: ${HARBOR_QQ_AI_BOT_INBOUND_DEDUPE_WINDOW_MS}
ONEBOT_INBOUND_DEDUPE_MAX_ENTRIES: ${HARBOR_QQ_AI_BOT_INBOUND_DEDUPE_MAX_ENTRIES}
ACP_CLIENT_NAME: qq-ai-bot
ACP_REUSE_SESSION: ${HARBOR_QQ_AI_BOT_ACP_REUSE_SESSION}
ACP_DEFAULT_SYSTEM_PROMPT: ${HARBOR_QQ_AI_BOT_DEFAULT_SYSTEM_PROMPT}
ACP_VERBOSE_MODE: ${HARBOR_QQ_AI_BOT_ACP_VERBOSE_MODE}
ACP_PERMISSION_STRATEGY: ${HARBOR_QQ_AI_BOT_ACP_PERMISSION_STRATEGY}
ACP_PROGRESS_THROTTLE_MS: ${HARBOR_QQ_AI_BOT_ACP_PROGRESS_THROTTLE_MS}
ACP_MAX_PROGRESS_UPDATES: ${HARBOR_QQ_AI_BOT_ACP_MAX_PROGRESS_UPDATES}
ACP_MAX_INBOUND_IMAGES: ${HARBOR_QQ_AI_BOT_ACP_MAX_INBOUND_IMAGES}
ACP_MAX_INBOUND_IMAGE_BYTES: ${HARBOR_QQ_AI_BOT_ACP_MAX_INBOUND_IMAGE_BYTES}
ports:
- ${HARBOR_QQ_AI_BOT_HOST_PORT}:8080
- ${HARBOR_QQ_AI_BOT_REVERSE_WS_HOST_PORT}:16700
volumes:
- ${HARBOR_QQ_AI_BOT_WORKSPACE}/data:/app/data
- ./services/qq-ai-bot/group-rules.json:/app/config/group-rules.json:ro
healthcheck:
test:
[
"CMD",
"node",
"-e",
"fetch('http://127.0.0.1:8080/readyz').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
networks:
- harbor-network
17 changes: 17 additions & 0 deletions services/qq-ai-bot/group-rules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"defaultSystemPrompt": "You are a stable, practical QQ AI assistant. Give the conclusion first, then the reason, risk, and next action. If you are unsure, say so plainly.",
"groups": {
"123456789": {
"name": "Tech discussion group",
"enabled": true,
"requireMention": true,
"systemPrompt": "You are serving a technical discussion group. Prefer conclusions, causes, risks, and concrete next steps."
},
"223344556": {
"name": "Ops on-call group",
"enabled": true,
"requireMention": false,
"systemPrompt": "You are serving an operations on-call group. Keep replies short and execution-oriented."
}
}
}
6 changes: 6 additions & 0 deletions services/qq-ai-bot/override.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Service-native overrides for qq-ai-bot.
# Use `harbor env qq-ai-bot KEY value` or edit this file directly.
# Replace the mock ACP runtime with your own agent by editing these values.
ACP_AGENT_COMMAND=node
ACP_AGENT_ARGS_JSON=["dist/examples/mock-acp-agent.js"]
ACP_AGENT_WORKDIR=/app