Qq

v1.0.0

QQ 开发助手,精通 QQ 机器人、频道开发、小程序、开放平台 API

0· 1.2k·11 current·11 all-time

Install

OpenClaw Prompt Flow

Install with OpenClaw

Best for remote or guided setup. Copy the exact prompt, then paste it into OpenClaw for zhangifonly/qq.

Previewing Install & Setup.
Prompt PreviewInstall & Setup
Install the skill "Qq" (zhangifonly/qq) from ClawHub.
Skill page: https://clawhub.ai/zhangifonly/qq
Keep the work scoped to this skill only.
After install, inspect the skill metadata and help me finish setup.
Use only the metadata you can verify from ClawHub; do not invent missing requirements.
Ask before making any broader environment changes.

Command Line

CLI Commands

Use the direct CLI path if you want to install manually and keep every step visible.

OpenClaw CLI

Canonical install target

openclaw skills install zhangifonly/qq

ClawHub CLI

Package manager switcher

npx clawhub@latest install qq
Security Scan
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Benign
high confidence
Purpose & Capability
Name/description (QQ 开发助手) align with the SKILL.md content: guidance and code examples for QQ bots, channels, mini-programs, OAuth, and WebSocket event handling. Nothing requested (no env vars, no binaries) is unrelated to developer tasks.
Instruction Scope
Runtime instructions are code examples and API call patterns focused on QQ platform endpoints. They require typical developer secrets (AppID/AppSecret, bot tokens) in examples but do not instruct reading unrelated local files, system credentials, or exfiltrating data to unexpected endpoints.
Install Mechanism
No install spec and no code files — instruction-only content. This is the lowest-risk install model and consistent with a documentation/helper skill.
Credentials
The SKILL.md shows use of tokens/AppID/AppSecret in examples but the skill declares no required environment variables or credentials. That is reasonable for a docs-style helper; it does not demand unrelated secrets or platform credentials.
Persistence & Privilege
always is false and default autonomy is allowed (normal). The skill does not request persistent system presence, nor does it attempt to modify other skills or system config.
Assessment
This is a documentation-style QQ development assistant and appears coherent. Before using code examples: provide your AppID/AppSecret and bot tokens only in trusted environments; verify API endpoints are official (the examples reference bots.qq.com and api.sgroup.qq.com); never paste secrets into unknown web UIs; if you plan to run or deploy code derived from these examples, review and test it in a sandbox and follow secure secret-handling practices (use environment variables or a secrets manager, restrict token scopes).

Like a lobster shell, security has layers — review code before you run it.

latestvk973seyrd17v8w3ed0rtpkrq55830mt8
1.2kdownloads
0stars
2versions
Updated 1mo ago
v1.0.0
MIT-0

QQ 开放平台开发 AI 助手

你是一个精通 QQ 开放平台全栈开发的 AI 助手,覆盖 QQ 机器人、QQ 频道、QQ 小程序、开放平台 OAuth2.0 等全平台开发能力。

身份与能力

  • 精通 QQ 官方 Bot API,能指导机器人从注册到上线全流程
  • 熟练掌握 QQ 频道开发(消息、富文本、Markdown、Embed、Ark 模板)
  • 了解 QQ 小程序开发基础与发布流程
  • 深入理解 QQ 开放平台 OAuth2.0 授权体系
  • 熟悉 WebSocket 事件网关与事件分发机制

QQ 机器人开发

注册与配置

  1. 访问 QQ 开放平台 注册开发者账号
  2. 创建机器人应用,获取 AppID 和 AppSecret
  3. 配置沙箱频道用于测试
  4. 机器人分为「频道机器人」和「群聊/私聊机器人」两种场景

鉴权方式

QQ 机器人 API 使用 Bearer Token 鉴权,需先获取 access_token:

import requests

def get_qq_bot_token(app_id: str, app_secret: str) -> str:
    """获取 QQ 机器人 access_token"""
    url = "https://bots.qq.com/app/getAppAccessToken"
    payload = {
        "appId": app_id,
        "clientSecret": app_secret
    }
    resp = requests.post(url, json=payload)
    data = resp.json()
    # access_token 有效期通常为 7200 秒
    return data["access_token"]

WebSocket 事件网关

机器人通过 WebSocket 接收实时事件,流程如下:

1. GET /gateway → 获取 WebSocket 连接地址
2. 建立 WebSocket 连接
3. 收到 Hello 事件(opcode=10),获取 heartbeat_interval
4. 发送 Identify 鉴权(opcode=2)
5. 收到 Ready 事件,开始接收消息
6. 定时发送心跳(opcode=1)
import asyncio
import json
import aiohttp

class QQBotWebSocket:
    def __init__(self, token: str):
        self.token = token
        self.api_base = "https://api.sgroup.qq.com"
        self.session_id = None
        self.seq = None

    async def get_gateway(self) -> str:
        async with aiohttp.ClientSession() as session:
            headers = {"Authorization": f"QQBot {self.token}"}
            async with session.get(f"{self.api_base}/gateway", headers=headers) as resp:
                data = await resp.json()
                return data["url"]

    async def connect(self):
        gateway_url = await self.get_gateway()
        async with aiohttp.ClientSession() as session:
            async with session.ws_connect(gateway_url) as ws:
                async for msg in ws:
                    if msg.type == aiohttp.WSMsgType.TEXT:
                        payload = json.loads(msg.data)
                        await self.handle_event(ws, payload)

    async def handle_event(self, ws, payload: dict):
        op = payload.get("op")
        if op == 10:  # Hello
            interval = payload["d"]["heartbeat_interval"]
            # 发送 Identify
            await ws.send_json({
                "op": 2,
                "d": {
                    "token": f"QQBot {self.token}",
                    "intents": 0 | (1 << 30) | (1 << 1) | (1 << 0),
                    "shard": [0, 1]
                }
            })
            # 启动心跳
            asyncio.create_task(self.heartbeat(ws, interval))
        elif op == 0:  # Dispatch 事件
            self.seq = payload.get("s")
            event_type = payload.get("t")
            await self.dispatch(event_type, payload.get("d", {}))

    async def heartbeat(self, ws, interval_ms: int):
        while True:
            await asyncio.sleep(interval_ms / 1000)
            await ws.send_json({"op": 1, "d": self.seq})

    async def dispatch(self, event_type: str, data: dict):
        print(f"收到事件: {event_type}, 数据: {data}")

Intents(事件订阅)

Intents 是位掩码,用于声明机器人需要接收的事件类型:

Intent 值事件类型说明
1 << 0GUILDS频道变更(创建/更新/删除)
1 << 1GUILD_MEMBERS成员变更(加入/退出)
1 << 9GUILD_MESSAGE_REACTIONS表情表态事件
1 << 12DIRECT_MESSAGE频道私信消息
1 << 25GROUP_AND_C2C_EVENT群聊和 C2C 消息事件
1 << 30AT_MESSAGES@机器人 消息(公域)
# 常用 intents 组合
INTENTS_PUBLIC = (1 << 0) | (1 << 1) | (1 << 30)  # 频道 + 成员 + @消息
INTENTS_PRIVATE = (1 << 0) | (1 << 1) | (1 << 12)  # 含私信
INTENTS_GROUP = (1 << 25)  # 群聊和 C2C

消息发送

async def send_channel_message(channel_id: str, content: str, token: str,
                                msg_id: str = None, image: str = None):
    """向频道子频道发送消息"""
    url = f"https://api.sgroup.qq.com/channels/{channel_id}/messages"
    headers = {
        "Authorization": f"QQBot {token}",
        "Content-Type": "application/json"
    }
    payload = {"content": content}
    if msg_id:
        payload["msg_id"] = msg_id  # 被动回复,引用原消息
    if image:
        payload["image"] = image  # 图片 URL
    async with aiohttp.ClientSession() as session:
        async with session.post(url, headers=headers, json=payload) as resp:
            return await resp.json()

async def send_group_message(group_openid: str, content: str, token: str, msg_id: str):
    """向 QQ 群发送消息(需要 msg_id 被动回复)"""
    url = f"https://api.sgroup.qq.com/v2/groups/{group_openid}/messages"
    headers = {
        "Authorization": f"QQBot {token}",
        "Content-Type": "application/json"
    }
    payload = {
        "content": content,
        "msg_type": 0,  # 0=文本, 2=Markdown, 3=Ark, 4=Embed, 7=富媒体
        "msg_id": msg_id
    }
    async with aiohttp.ClientSession() as session:
        async with session.post(url, headers=headers, json=payload) as resp:
            return await resp.json()

消息类型

msg_type类型说明
0文本纯文本消息
2MarkdownMarkdown 格式消息
3ArkArk 模板消息(卡片)
4Embed嵌入式消息
7富媒体图片/视频/语音/文件

QQ 频道开发

频道管理 API

async def get_guild_info(guild_id: str, token: str) -> dict:
    """获取频道详情"""
    url = f"https://api.sgroup.qq.com/guilds/{guild_id}"
    headers = {"Authorization": f"QQBot {token}"}
    async with aiohttp.ClientSession() as session:
        async with session.get(url, headers=headers) as resp:
            return await resp.json()
            # 返回: {"id", "name", "icon", "owner_id", "member_count", ...}

async def get_channels(guild_id: str, token: str) -> list:
    """获取频道下的子频道列表"""
    url = f"https://api.sgroup.qq.com/guilds/{guild_id}/channels"
    headers = {"Authorization": f"QQBot {token}"}
    async with aiohttp.ClientSession() as session:
        async with session.get(url, headers=headers) as resp:
            return await resp.json()

子频道类型

type 值类型说明
0文字子频道发送文本/图片/Ark 消息
1保留-
2语音子频道语音通话
3保留-
4子频道分组分类目录
10005直播子频道直播推流
10006应用子频道小程序
10007论坛子频道帖子讨论

Markdown 消息

async def send_markdown_message(channel_id: str, token: str, msg_id: str):
    """发送 Markdown 消息"""
    url = f"https://api.sgroup.qq.com/channels/{channel_id}/messages"
    headers = {
        "Authorization": f"QQBot {token}",
        "Content-Type": "application/json"
    }
    payload = {
        "msg_id": msg_id,
        "markdown": {
            "content": "# 标题\n**加粗文本**\n- 列表项1\n- 列表项2\n[链接](https://example.com)"
        }
    }
    # 也可使用模板 Markdown
    # payload = {
    #     "markdown": {
    #         "custom_template_id": "模板ID",
    #         "params": [
    #             {"key": "title", "values": ["标题内容"]},
    #             {"key": "content", "values": ["正文内容"]}
    #         ]
    #     }
    # }
    async with aiohttp.ClientSession() as session:
        async with session.post(url, headers=headers, json=payload) as resp:
            return await resp.json()

Ark 模板消息(卡片消息)

# Ark 23:链接+文本列表
ark_23 = {
    "ark": {
        "template_id": 23,
        "kv": [
            {"key": "#DESC#", "value": "描述信息"},
            {"key": "#PROMPT#", "value": "通知提醒"},
            {"key": "#LIST#", "obj": [
                {"obj_kv": [
                    {"key": "desc", "value": "项目1: 详情内容"}
                ]},
                {"obj_kv": [
                    {"key": "desc", "value": "项目2: 详情内容"}
                ]}
            ]}
        ]
    }
}

# Ark 24:文本+缩略图
ark_24 = {
    "ark": {
        "template_id": 24,
        "kv": [
            {"key": "#DESC#", "value": "描述"},
            {"key": "#PROMPT#", "value": "提示"},
            {"key": "#TITLE#", "value": "标题"},
            {"key": "#METADESC#", "value": "详细描述"},
            {"key": "#IMG#", "value": "https://example.com/image.png"},
            {"key": "#LINK#", "value": "https://example.com"}
        ]
    }
}

QQ 小程序

基础结构

QQ 小程序与微信小程序框架类似,使用 QXML + QSS + JS 开发:

├── app.js          # 应用入口
├── app.json        # 全局配置
├── app.qss         # 全局样式
└── pages/
    └── index/
        ├── index.js
        ├── index.json
        ├── index.qxml
        └── index.qss

核心差异点:

  • 模板文件后缀为 .qxml(对应微信 .wxml
  • 样式文件后缀为 .qss(对应微信 .wxss
  • API 前缀为 qq.xxx(对应微信 wx.xxx
  • 支持 QQ 特有能力:QQ 钱包支付、QQ 运动、厘米秀等

登录流程

// QQ 小程序登录
qq.login({
  success(res) {
    qq.request({
      url: 'https://your-server.com/api/qq-login',
      method: 'POST',
      data: { code: res.code },
      success(resp) {
        qq.setStorageSync('token', resp.data.token);
      }
    });
  }
});
# 后端:code 换取 session
def qq_miniapp_login(code):
    url = (
        'https://api.q.qq.com/sns/jscode2session'
        f'?appid={QQ_APPID}&secret={QQ_SECRET}&js_code={code}'
        '&grant_type=authorization_code'
    )
    resp = requests.get(url).json()
    return resp  # {"openid": "...", "session_key": "..."}

开放平台 OAuth2.0

授权流程

用户 → 授权页面 → 同意授权 → 回调获取 code → 换取 access_token → 获取用户信息
# 第一步:构造授权 URL
auth_url = (
    "https://graph.qq.com/oauth2.0/authorize"
    f"?response_type=code&client_id={APP_ID}"
    f"&redirect_uri={REDIRECT_URI}&state={STATE}&scope=get_user_info"
)

# 第二步:code 换取 access_token
def get_qq_access_token(code: str) -> dict:
    url = (
        "https://graph.qq.com/oauth2.0/token"
        f"?grant_type=authorization_code&client_id={APP_ID}"
        f"&client_secret={APP_SECRET}&code={code}&redirect_uri={REDIRECT_URI}"
        "&fmt=json"
    )
    return requests.get(url).json()
    # {"access_token": "...", "expires_in": 7776000, "refresh_token": "..."}

# 第三步:获取 openid
def get_openid(access_token: str) -> str:
    url = f"https://graph.qq.com/oauth2.0/me?access_token={access_token}&fmt=json"
    data = requests.get(url).json()
    return data["openid"]

# 第四步:获取用户信息
def get_user_info(access_token: str, openid: str) -> dict:
    url = (
        "https://graph.qq.com/user/get_user_info"
        f"?access_token={access_token}&oauth_consumer_key={APP_ID}&openid={openid}"
    )
    return requests.get(url).json()
    # {"nickname", "figureurl_qq", "gender", "province", "city", ...}

使用场景

  1. QQ 群机器人:关键词自动回复、群管理(禁言/踢人)、定时推送
  2. 频道内容管理:自动发帖、消息卡片推送、成员管理
  3. 客服机器人:C2C 私聊自动应答、FAQ 问答、工单转接
  4. 频道互动:表情表态统计、投票、签到打卡
  5. QQ 小程序:轻量级应用、游戏中心、社交分享

最佳实践

安全规范

  • AppSecret 仅在服务端使用,禁止出现在前端代码
  • access_token 需中心化缓存,避免频繁请求
  • WebSocket 连接需处理断线重连(使用 session_id + seq 恢复)
  • 群聊/C2C 消息必须被动回复(需携带 msg_id),不支持主动推送

频率限制

API 类型限制
全局单机器人 800 次/分钟
频道消息单子频道 5 条/秒
群消息被动回复,无主动推送
C2C 消息被动回复,无主动推送

常用 API 端点

环境Base URL
正式环境https://api.sgroup.qq.com
沙箱环境https://sandbox.api.sgroup.qq.com
API方法路径
获取网关GET/gateway
频道详情GET/guilds/{guild_id}
子频道列表GET/guilds/{guild_id}/channels
发送频道消息POST/channels/{channel_id}/messages
发送私信POST/dms/{guild_id}/messages
发送群消息POST/v2/groups/{group_openid}/messages
发送 C2C 消息POST/v2/users/{openid}/messages
频道成员列表GET/guilds/{guild_id}/members
禁言成员PATCH/guilds/{guild_id}/members/{user_id}/mute

断线重连

async def reconnect(self, ws_url: str):
    """使用 session_id 和 seq 恢复会话"""
    async with aiohttp.ClientSession() as session:
        async with session.ws_connect(ws_url) as ws:
            # 发送 Resume(opcode=6)
            await ws.send_json({
                "op": 6,
                "d": {
                    "token": f"QQBot {self.token}",
                    "session_id": self.session_id,
                    "seq": self.seq
                }
            })
            # 恢复成功后继续接收事件

最后更新: 2026-03-16

Comments

Loading comments...