Skip to content

varpress/qqbot-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

QQ Bot Java SDK

openclaw-qqbot 抽取并改写的 QQ Bot API Java SDK,用于在 Java 应用中直接调用 QQ Bot 官方 API(鉴权、Gateway 连接、消息收发、富媒体、定时消息等),不依赖 OpenClaw 本体。

JDK Version License Upstream


✨ 功能概览

模块 说明
qqbot.sdk.api.Api QQ Bot HTTP API 封装:获取 AccessToken、获取 Gateway URL、发送文本 / 图片 / 语音 / 视频 / 文件、后台 Token 刷新(多 AppID 隔离缓存)
qqbot.sdk.gateway.Gateway Gateway 入口:自动重连、会话恢复(Identify/Resume)、心跳、限流重试、统一的入站消息事件
qqbot.sdk.gateway.QQGatewayClient WebSocket 协议实现:Hello / Identify / Resume / Heartbeat、事件分发、按用户并发消息队列
qqbot.sdk.gateway.InboundMessageEvent 入站消息统一结构:C2C / 频道 / 群聊,附带附件列表、本地路径映射、可选富化内容
qqbot.sdk.outbound.Outbound 出站消息封装:被动回复 / 主动消息 / 富媒体 / 定时消息(Cron 载荷)
qqbot.sdk.session_store.SessionStore Session 持久化:在本地保存 Gateway sessionId + lastSeq,支持 5 分钟过期和节流写盘
qqbot.sdk.utils.* 工具集合:跨平台路径处理、文件读写与大小校验、音频转换、上传缓存、QQ 表情标签解析等
qqbot.sdk.refindex.RefIndexStore REFIDX 引用索引存储:持久化 QQ REFIDX_* 索引 → 消息摘要,供后续引用消息解析

Java SDK 只负责“对接 QQ Bot API”,不涉及 OpenClaw 的插件框架和命令行逻辑,但在设计上与 openclaw-qqbot JS 插件保持一一对应,便于阅读 JS 源码时进行功能比对。


🔧 环境要求

  • JDK 21+
  • 构建工具推荐:Maven / Gradle
  • 依赖:
    • Jackson(com.fasterxml.jackson.core:jackson-databind 等,用于 JSON 序列化/反序列化)
    • org.java-websocket:Java-WebSocket(用于 Gateway WebSocket 连接)

🚀 快速上手

1. 引入依赖

以 Maven 为例(坐标请根据你实际发布到的仓库调整,这里仅示意):

<dependency>
    <groupId>qqbot</groupId>
    <artifactId>qqbot-sdk</artifactId>
    <version>1.0.0</version>
</dependency>

2. 启动 Gateway 并监听入站消息

import qqbot.sdk.gateway.Gateway;
import qqbot.sdk.gateway.GatewayContext;
import qqbot.sdk.gateway.GatewayMessageHandler;
import qqbot.sdk.gateway.InboundMessageEvent;
import qqbot.sdk.types.ResolvedQQBotAccount;

import java.util.concurrent.atomic.AtomicBoolean;

public class Demo {
    public static void main(String[] args) {
        // 1. 构造账号配置(通常来自你自己的配置文件)
        ResolvedQQBotAccount account = new ResolvedQQBotAccount();
        account.setAccountId("default");
        account.setAppId(System.getenv("QQBOT_APPID"));
        account.setClientSecret(System.getenv("QQBOT_SECRET"));
        account.setMarkdownSupport(true);

        // 2. 构造 Gateway 上下文
        AtomicBoolean abort = new AtomicBoolean(false);
        GatewayContext ctx = new GatewayContext();
        ctx.setAccount(account);
        ctx.setAbortSignal(abort::get);
        ctx.setLog(new GatewayContext.GatewayLog() {
            @Override public void info(String msg) { System.out.println(msg); }
            @Override public void error(String msg) { System.err.println(msg); }
            @Override public void debug(String msg) { System.out.println("[DEBUG] " + msg); }
        });
        ctx.setMessageHandler(new GatewayMessageHandler() {
            @Override
            public void onMessage(GatewayContext context, InboundMessageEvent event) {
                // event.getContent() 已包含表情解析与可选的语音转写
                System.out.println("收到消息: type=" + event.getType()
                        + ", from=" + event.getSenderId()
                        + ", content=" + event.getContent());
            }
        });

        // 3. 启动 Gateway(内部自动重连与会话恢复)
        Gateway gateway = new Gateway(ctx);
        gateway.startGateway();
    }
}

3. 仅使用 HTTP API 发送消息(不建连)

import qqbot.sdk.api.Api;

public class SendOnceDemo {
    public static void main(String[] args) throws Exception {
        String appId = System.getenv("QQBOT_APPID");
        String secret = System.getenv("QQBOT_SECRET");
        String openid = "USER_OPENID";

        // 仅调用 HTTP API 时,建议先初始化 markdown 支持开关
        Api.initApiConfig(true);

        String token = Api.getAccessToken(appId, secret);
        Api.sendC2CMessage(token, openid, "你好,这是一条来自 Java SDK 的消息", null);
    }
}

💬 引用消息上下文(REFIDX_*)

QQ 的引用消息事件中不会直接携带被引用消息的正文,而是通过 REFIDX_* 索引标识;服务端在发送消息成功时,也会在响应的 ext_info.ref_idx 字段里返回当前消息的索引。

Java SDK 按照 JS 版本的设计,提供了完整的引用索引链路:

  • 入站侧

    • C2CMessageEvent / GuildMessageEvent / GroupMessageEvent 都映射了 message_scene.ext 数组。
    • QQGatewayClient 会从 ext 中解析:
      • ref_msg_idx=REFIDX_xxx → 当前消息引用的那条历史消息(被引用消息的索引)。
      • msg_idx=REFIDX_yyy → 当前消息自身的索引。
    • 解析结果注入到 InboundMessageEvent
      • event.getRefMsgIdx():被引用消息的索引。
      • event.getMsgIdx():当前消息自身的索引。
  • 出站侧

    • MessageResponse 映射了响应体中的 ext_info.ref_idx
    • OutboundResult 增加了 refIdx 字段,Outbound 在内部从 HTTP 返回中提取赋值。
  • 持久化存储qqbot.sdk.refindex.RefIndexStore

    • 存储文件位置:
      • Platform.getDataDir("data") 目录下的 ref-index.jsonl,通常为:
        • Linux/macOS: ~/.qqbot-sdk/data/ref-index.jsonl
        • Windows: %USERPROFILE%\.qqbot-sdk\data\ref-index.jsonl
    • 设计:
      • 内存 Map + JSONL 追加写,进程重启后自动加载恢复。
      • 7 天 TTL,超过后自动失效并在适当时机 compact。
      • 最大 50,000 条记录,超出后按时间自动淘汰最旧的数据。
    • 核心 API:
      • setRefIndex(String refIdx, RefIndexEntry entry):写入索引。
      • RefIndexEntry getRefIndex(String refIdx):读取被引用消息摘要。
      • String formatRefEntryForAgent(RefIndexEntry entry):格式化为适合注入大模型上下文的一段说明文字。

应用层可以据此实现“带引用上下文的对话”,示意代码:

import qqbot.sdk.refindex.RefIndexStore;

public void onMessage(GatewayContext ctx, InboundMessageEvent event) {
    String userContent = event.getContent();

    // 如果用户引用了历史消息
    String quotedSummary = null;
    if (event.getRefMsgIdx() != null) {
        RefIndexStore.RefIndexEntry ref = RefIndexStore.getRefIndex(event.getRefMsgIdx());
        if (ref != null) {
            quotedSummary = RefIndexStore.formatRefEntryForAgent(ref);
        }
    }

    // 将 quotedSummary 注入到你构造的 prompt 中,交给大模型
}

注意:Java SDK 不强制自动写入“出站消息索引”,你可以在自己封装的发送逻辑中,基于 OutboundResult.getRefIdx() 手动调用 RefIndexStore.setRefIndex,或在需要时新增一层封装完成这一工作,从而完全复刻 JS 版本的行为。


📦 Session 与 Token 管理

  • Token 管理(qqbot.sdk.api.Api

    • AccessToken 以 appId 为 key 保存在内存 Map 中,带 5 分钟安全缓冲。
    • 采用“singleflight”模式:同一 appId 下并发刷新 Token 时只会发出一次 HTTP 请求,其他调用复用同一个 Future
    • 支持后台刷新:startBackgroundTokenRefresh(appId, clientSecret, options) / stopBackgroundTokenRefresh(appId)
  • Session 持久化(qqbot.sdk.session_store.SessionStore + Gateway

    • Gateway 启动时会尝试从本地加载上次保存的 sessionIdlastSeq,尽量使用 Resume 而非重新 Identify。
    • Session 写盘有 5 分钟过期和 1 秒节流机制,避免频繁 IO。

🧪 示例与扩展

仓库中包含若干脚本与测试用例(位于 src/test/javasrc/main/java/qqbot/sdk/scripts),涵盖:

  • 仅使用 HTTP API 单次发消息。
  • 启动 Gateway 并打印入站事件。
  • 使用 Cron 载荷(QQBOT_CRON:...)发送定时提醒消息。

你可以基于这些示例快速扩展为:

  • 自己的 QQ Bot 后端服务(Spring Boot / Jakarta EE 等)。
  • 将 QQ 作为入口,对接你已有的对话/工作流系统。

📝 License

本 SDK 源自 openclaw-qqbot 的实现思路与协议封装,保持开源协议一致(MIT)。如在使用过程中遇到问题或发现与 JS 版本行为不一致,欢迎在原仓库或你自己的仓库中提 Issue。

About

从 `openclaw-qqbot` 抽取并改写的 **QQ Bot API Java SDK**,用于在 Java 应用中直接调用 QQ Bot 官方 API(鉴权、Gateway 连接、消息收发、富媒体、定时消息等),不依赖 OpenClaw 本体。

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages