文章

QStory 开发文档

发布于 2026-04-05 02:26

QStory Java脚本开发文档(新)

适用于新版本 QStory 脚本开发。

  • 最近一次文档更新:2026-04-01
  • 维护与 Bug 反馈:请通过能联系到我的渠道反馈
  • 变更说明:QStory 脚本变更

1. 开发前必读

1.1 环境限制

  1. 脚本环境不支持注解,写入后会直接导致加载失败。
  2. 可使用 Java 标准类库与 Android 标准类库,例如 org.json.JSONObjectTextView,但需要自行
    import
  3. 脚本中的实体对象,如 GroupInfoGroupMemberInfo,通常可直接用 Object 指代,并直接访问字段。
  4. Java 运行环境为 JDK 9,不支持较新的 API。
  5. 频道相关内容当前没有系统性维护

1.2 编写方式

  • 回调方法直接定义在文件根作用域,不需要写在类中。
  • 全局变量可直接引用。
  • 文档中的示例以 main.java 为主文件。

2. 脚本目录结构

运行脚本时至少需要以下文件:

  • main.java:点击加载时执行的主脚本文件。
  • desc.txt:脚本描述文件,用于列表展示。
  • info.prop:脚本信息文件,格式为 key=value

2.1 info.prop 必填字段

示例 说明
name name=脚本名称 脚本名称
type type=1 直接写 1
version version=1.0 版本号
author author=作者名 作者名
id id=脚本唯一ID 脚本唯一标识,必须唯一
date date=2025-12-1 格式固定为 yyyy-M-d,日期过旧可能导致脚本无法加载
tags tags=群聊辅助,娱乐功能 当前常见标签:群聊辅助娱乐功能功能扩展综合脚本官方脚本,也可自定义

info.prop

name=脚本名称
type=1
version=1.0
author=作者名
id=脚本唯一ID
date=2025-12-1
tags=群聊辅助,娱乐功能

2.2 预览图目录

  • 在脚本目录下创建 images/ 文件夹。
  • icon.png 作为图标。
  • 其他图片作为预览图。
  • 预览图按 A-Za-z0-9 排序,后缀不限。
├── info.prop
├── desc.txt
├── main.java
└── images/
    ├── icon.png
    ├── preview1.png
    └── preview2.png

3. 全局变量

以下变量可直接在脚本中使用:

变量 类型 说明
myUin String 当前用户 QQ 号
context Context QQ 全局上下文对象,即 android.content.Context
appPath String 脚本运行时的相对目录
loader ClassLoader QQ 的类加载器
pluginID String 当前脚本 ID

示例:

toast("当前QQ:"+myUin);

4. 回调方法

回调直接定义在文件根作用域即可,相关事件触发时会自动调用。

基础示例:

void onMsg(Object msg) {
    toast("消息内容:" + msg.MessageContent);
}

4.1 onMsg(MessageData msg)

收到消息时调用。

MessageData 常用字段如下:

字段 类型 说明
MessageContent String 消息内容。文本、图片下载地址、语音 MD5、卡片代码等
GroupUin String 群号。群消息、私聊消息、频道消息时有效
PeerUin String 私聊时可用,通常为对方 QQ 号
UserUin String 发送者 QQ 号
MessageType int 消息类型:1 文本/图片,2 卡片,3 图文,4 语音,5 文件,6 回复
IsGroup boolean 是否群消息。频道消息中也可能为 true
IsChannel boolean 是否频道消息
SenderNickName String 发送者昵称
MessageTime long 消息时间戳,单位毫秒
mAtList ArrayList<String> 被艾特列表
IsSend boolean 是否为自己发送的消息
FileName String 文件名,仅群文件消息有效
FileSize long 文件大小,仅群文件消息有效
LocalPath String 本地文件路径,仅语音消息有效
ReplyTo String 被回复用户账号,仅回复消息有效
RecordMsg MessageData 被回复的原消息
GuildID String 频道 ID,仅频道消息有效
ChannelID String 子频道 ID,仅频道消息有效
PicList String[] 图片 MD5 列表
PicUrlList ArrayList<String> 图片链接列表
msg Object 未解析的原始消息对象

4.2 其他回调

void onForbiddenEvent(String groupUin, String userUin, String opUin, long time)

成员被禁言时调用。

  • groupUin:群号
  • userUin:被禁言的用户 QQ
  • opUin:执行禁言的管理员 QQ
  • time:禁言时长,单位秒

void onTroopEvent(String groupUin, String userUin, int type)

发生进群或退群时调用。

  • groupUin:群号
  • userUin:用户 QQ
  • type=2:进群
  • type=1:退群

void onClickFloatingWindow(int type, String uin)

脚本悬浮窗打开时调用。

  • type=1:私聊
  • type=2:群聊
  • uin:私聊时为 QQ 号,群聊时为群号

通常配合 addTemporaryItem(String itemName, String callbackName) 使用。

String getMsg(String msg, String peerUin, int chatType)

点击发送按钮发送消息时调用。

  • 仅支持处理纯文本消息
  • 参数 1:即将发送的文本
  • 参数 2:好友号码或群号
  • 参数 3:类型,2 为群聊,1100 代表私聊
  • 返回值:返回后的文本会替换原文本内容

void onCreateMenu(MessageData msg)

长按消息准备创建消息长按菜单时调用。

  • msgonMsg(MessageData msg) 中的对象一致
  • 通常用于结合 addMenuItem(...) 动态创建一次性长按菜单

void callbackOnRawMsg(Object msg)

收到未解析原始消息时调用,包括灰字、文本等。

  • 对应 QQ 类:com.tencent.qqnt.kernel.nativeinterface.MsgRecord
  • 需要自行解析

void onLoad()

脚本加载完成时调用。

void onUnLoad()

脚本取消加载时调用。


5. API 方法

5.1 发送消息相关

默认约定:

  • 参数 1 为群号
  • 参数 2 为 QQ 号
  • 参数 3 为内容或路径
  • QQ 号为空时发送群消息
  • 群号为空时发送私聊消息
方法 说明
sendMsg(String groupUin, String userUin, String msg) 发送文本、图片或图文消息。图文格式可写 [PicUrl=图片本地或网络地址],艾特格式可写 [AtQQ=QQ号]
sendPic(String groupUin, String userUin, String path) 发送单张图片,本地或网络地址均可
sendSticker(String groupUin, String userUin, String path, String summary) 以表情方式发送图片,summary 不填时默认显示 [动画表情]
sendCard(String groupUin, String userUin, String card) 发送 JSON 卡片代码
sendReply(String groupUin, Object msg, String text) 发送回复消息,仅支持群聊
sendFile(String groupUin, String userUin, String path) 发送文件
sendVoice(String groupUin, String userUin, String path) 发送语音
sendVideo(String groupUin, String userUin, String path) 发送视频
sendLike(String userUin, int count) 给指定 QQ 点赞
sendPai(String groupUin, String userUin) 拍一拍或戳一戳。群聊时传群号,私聊时群号留空
replyEmoji(Object msg, String emojiId) 对消息发送表情回应
replyEmoji(Object target, int emojiType, String emojiId) 对消息发送表情回应。已知原生表情 emojiType=2,QQ 自带表情 emojiType=1
forwardMsg(String groupUin, String userUin, Object msg) 转发消息
sendProto(String cmd, String jsonBody) 发送 ProtoBuf 消息,实验性方法

5.2 群聊操作

方法 说明
setCard(String groupUin, String userUin, String name) 设置群名片,仅管理员可用,当前未维护
setTitle(String groupUin, String userUin, String title) 设置头衔,仅群主可用
revokeMsg(Object msg) 撤回消息。只能撤回自己发的消息,或管理员撤回群员消息
deleteMsg(Object msg) 删除消息
forbidden(String groupUin, String userUin, int time) 禁言,单位秒。管理员可用;全体禁言时用户账号可留空
kick(String groupUin, String userUin, boolean isBlack) 踢出群成员,isBlack 表示是否禁止再次申请

补充说明:

  • 全体禁言填1以上的时间,禁言结束后,可能会出现“显示全体禁言但仍可发消息”的假全体禁言状态。

5.3 信息获取

说明:获取的数据越多,耗时越高。通常可以直接使用 Object,无需强转。

消息与成员基础信息

方法 说明
List<MessageData> getMessageList(String groupUin, String userUin, int count) 获取消息列表,结构与 onMsgMessageData 一致
String getMemberName(String groupUin, String uin) 获取群成员名称

群信息

方法 说明
ArrayList<GroupInfo> getGroupList() 获取群信息列表
GroupInfo getGroupInfo(String groupUin) 获取指定群信息

GroupInfo 字段:

字段 类型 说明
GroupUin String 群号
GroupName String 群名
GroupOwner String 群主账号
AdminList String[] 管理员列表,包含群主;不一定实时,通常约 30 分钟刷新一次
IsOwnerOrAdmin boolean 当前账号在该群是否为群主或管理员
sourceInfo Object 原对象,对应 com.tencent.mobileqq.data.troop.TroopMemberInfo

群成员信息

方法 说明
ArrayList<GroupMemberInfo> getGroupMemberList(String groupUin) 获取群成员信息列表
GroupMemberInfo getMemberInfo(String groupUin, String uin) 获取指定群成员信息

GroupMemberInfo 字段:

字段 类型 说明
UserUin String 成员账号
NickName String 群内昵称
UserName String 成员名字,通常为好友备注
UserLevel int 群聊等级
Join_Time long 加群时间
Last_AvtivityTime long 最后发言时间,不一定实时刷新
sourceInfo Object 原对象,对应 com.tencent.mobileqq.data.troop.TroopInfo
IsOwner boolean 是否群主
IsAdmin boolean 是否管理员

禁言信息

方法 说明
ArrayList<ForbiddenInfo> getForbiddenList(String groupUin) 获取群内被禁言成员列表

ForbiddenInfo 字段:

字段 类型 说明
UserUin String 成员账号
UserName String 成员名字
Endtime long 禁言结束时间戳

好友信息

方法 说明
ArrayList<FriendInfo> getFriendList() 获取好友列表
boolean isFriend(String uin) 判断是否为好友
List<NewFriendInfo> getNewFriendList() 新版获取好友列表方法
NewFriendInfo getNewFriendInfo(String uin) 获取单个好友信息

FriendInfo 字段:

字段 类型 说明
uin String QQ 号
name String QQ 昵称
remark String 备注
isVip boolean 是否会员
vipLevel int 会员等级

NewFriendInfo 结构:

class NewFriendInfo {
    public String uin;      // QQ号
    public String nickname; // QQ昵称
    public String remark;   // 备注
    public int sex;         // 性别
    public int age;         // 年龄
}

5.4 简单数据存储

方法 说明
void putString(String configName, String key, String value) 存储字符串
String getString(String configName, String key) 读取字符串
String getString(String configName, String key, String def) 读取字符串并指定默认值
void putInt(String configName, String key, int value) 存储整数
int getInt(String configName, String key, int def) 读取整数
void putLong(String configName, String key, long value) 存储长整数
long getLong(String configName, String key, long def) 读取长整数
void putBoolean(String configName, String key, boolean value) 存储布尔值
boolean getBoolean(String configName, String key, boolean def) 读取布尔值
void putFloat(String configName, String key, float value) 存储浮点数
float getFloat(String configName, String key, float def) 读取浮点数
void putDouble(String configName, String key, double value) 存储双精度浮点数
double getDouble(String configName, String key, double def) 读取双精度浮点数

5.5 SKey 相关

方法 说明
String getGroupRKey() 获取群聊 rkey
String getFriendRKey() 获取私聊 rkey
String getSkey() 获取标准 skey
String getRealSkey() 获取可能更真实的 skey
String getPskey(String url) 获取指定域名相关 pskey
String getPT4Token(String str) 懂的都懂
String getGTK(String str) 可能懂了
long getBKN(String pskey) 不懂

5.6 其他能力

方法 说明
Activity getActivity() 获取当前 QQ 顶层 Activity;若 QQ 在后台则返回 null
toast(Object content) 弹出 Toast
load(String path) 在当前脚本环境再加载一个 Java 文件。建议使用 load(appPath + "/dir/Utils.java");
loadJar(String jarPath) 加载 Jar
loadDex(String path) 加载 Dex
eval(String code) 热加载一段 Java 代码
error(Throwable throwable) 将异常输出到脚本目录
log(Object content) 将日志输出到脚本目录

HTTP 方法

方法 说明
String httpGet(String url) 内置 HTTP GET,请求浏览器可访问的链接内容
String httpGet(String url, Map<String, String> headers) 带请求头的 GET
String httpPost(String url, Map<String, String> data) 内置 HTTP POST,发送表单
String httpPost(String url, Map<String, String> headers, Map<String, String> data) 带请求头的 POST 表单
String httpPostJson(String url, String data) 发送 application/json; charset=utf-8 请求
String httpPostJson(String url, Map<String, String> headers, String data) 带请求头的 JSON POST
void httpDownload(String url, String path) 下载文件到脚本目录内的相对路径
void httpDownload(String url, String path, Map<String, String> headers) 带请求头下载文件

HTTP 相关注意事项:

  • Map 参数和响应结果仅支持字符串。
  • GETPOST 请求异常时会返回空字符串。
  • 文件下载失败时会抛出异常。
  • 下载路径必须是脚本目录中的相对路径,其他路径 QQ 可能无权限读写。

OCR 方法

方法 说明
String scanImageText(String path) 扫描图片中的文字,直接返回文本
List<OcrText> scanImageTextDetail(String path) 返回更详细的 OCR 结果,包括文本、方向、可信度、位置

OcrText 结构:

public class OcrText {
    public Float confidence;      // 置信度
    public int orientation;       // 方向
    public List<Point> points;    // 位置,android.graphics.Point
    public String text;           // 文本
    public List<OcrTextResult> textList; // 更细粒度的文本列表

    public static class OcrTextResult {
        public Float confidence;  // 置信度
        public String text;       // 单个字符
        public int id;            // ID
    }
}

文件操作

写入操作会自动创建父级目录和目标文件。

方法 说明
String readFileText(String path) 读取文本文件
void writeTextToFile(String path, String text) 覆盖写入文本
void writeTextAppendToFile(String path, String text) 追加写入文本
byte[] readFileBytes(String path) 读取字节
void writeBytesToFile(String path, byte[] bytes) 写入字节

6. 菜单与交互

6.1 悬浮窗菜单

方法 说明
String addItem(String name, String callbackName) 添加一个常驻菜单,由模块显示在聊天窗口中
void addTemporaryItem(String itemName, String callbackName) 添加一个临时菜单,脚本菜单弹窗关闭后自动删除
void removeItem(String itemID) 删除菜单

规则:

  • 参数 1 为显示名称。
  • 参数 2 为回调方法名。
  • 被调用的回调方法需要提供 3 个参数:群号、QQ 号、聊天类型。
  • 私聊 chatType=1,群聊 chatType=2

示例:

addItem("开关加载提示","toggleLoadToast");

public void toggleLoadToast(String groupUin, String uin, int chatType) {
    if (getString("加载提示", "开关") == null) {
        putString("加载提示", "开关", "关");
        toast("已关闭加载提示");
    } else {
        putString("加载提示", "开关", null);
        toast("已开启加载提示");
    }
}

if(

getString("加载提示","开关") ==null){

toast("加载成功");
}

6.2 常规开关功能示例

String configName = "开关";

addItem("开关","open");

public void open(String groupUin, String uin, int chatType) {
    if (chatType != 2) {
        toast("不支持私聊开启");
        return;
    }

    if (getBoolean(configName, groupUin, false)) {
        putBoolean(configName, groupUin, false);
        toast("已关闭" + groupUin);
    } else {
        putBoolean(configName, groupUin, true);
        toast("已开启" + groupUin);
    }
}

public boolean isOpen(String groupUin) {
    return getBoolean(configName, groupUin, false);
}

void onTroopEvent(String groupUin, String userUin, int type) {
    if (!isOpen(groupUin)) {
        return;
    }
    if (type == 2) {
        sendMsg(groupUin, "", "有人加入:" + userUin);
    }
    if (type == 1) {
        sendMsg(groupUin, "", "有人退出:" + userUin);
    }
}

6.3 长按消息菜单

addMenuItem 只能在 onCreateMenu(MessageData msg) 中使用。

方法 说明
String addMenuItem(String name, String callbackName) 在长按消息菜单中添加选项

规则:

  • 参数 1 为菜单名称。
  • 参数 2 为回调方法名。
  • 回调方法只需要 1 个参数,参数类型与 onMsg(MessageData msg) 中的 msg 一致。

示例:

void onCreateMenu(MessageData msg) {
    if (msg.IsGroup) {
        addMenuItem("仅群", "showGroup");
    }
}

void showGroup(MessageData msg) {
    toast("提示在" + msg.GroupUin);
}

6.4 当前窗口信息

方法 说明
int getChatType() 获取当前聊天类型,1 为私聊,2 为群聊
String getCurrentGroupUin() 获取当前群号;若当前为私聊则返回空
String getCurrentFriendUin() 获取当前好友 QQ;若当前为群聊则返回空

7. 完整示例

下面是一个基础的 QStory Java 脚本示例:

public void onMsg(Object msg) {
    String text = msg.MessageContent;
    String qq = msg.UserUin;
    String qun = msg.GroupUin;

    if (text.equals("菜单") && qq.equals(myUin)) {
        String reply = "TG频道:https://t.me/QStoryPlugin\n交流群:979938489\n---------\n这是菜单,你可以发送下面的指令来进行测试\n艾特我\n回复我\n私聊我";

        if (msg.IsGroup) {
            sendMsg(qun, "", reply);
        } else {
            sendMsg("", qq, reply);
        }
    }

    if (text.equals("艾特我") && msg.IsGroup && qq.equals(myUin)) {
        sendMsg(qun, "", "[AtQQ=" + qq + "] 嗯呐");
    }

    if (text.equals("回复我") && msg.IsGroup && qq.equals(myUin)) {
        sendReply(qun, msg, "好啦");
    }

    if (text.equals("私聊我")) {
        sendMsg("", qq, "我已经私聊你咯");
    }

    if (
            msg.IsSend
                    && msg.MessageContent.matches("禁言 ?@[\\s\\S]+[0-9]+(天|分|时|小时|分钟|秒)")
                    && msg.mAtList.size() >= 1
    ) {
        int banTime = parseTimeByMessage(msg);
        if (banTime >= 60 * 60 * 24 * 30 + 1) {
            sendMsg(msg.GroupUin, "", "请控制在30天以内");
            return;
        }

        for (String atUin : msg.mAtList) {
            forbidden(msg.GroupUin, atUin, banTime);
        }
    }
}

public int parseTimeByMessage(Object msg) {
    int timeStartIndex = msg.MessageContent.lastIndexOf(" ");
    String date = msg.MessageContent.substring(timeStartIndex + 1).trim();
    String t = "";

    if (date != null && !"".equals(date)) {
        for (int i = 0; i < date.length(); i++) {
            if (date.charAt(i) >= 48 && date.charAt(i) <= 57) {
                t += date.charAt(i);
            }
        }
    }

    int time = Integer.parseInt(t);
    if (date.contains("天")) {
        return time * 60 * 60 * 24;
    } else if (date.contains("时") || date.contains("小时")) {
        return 60 * 60 * time;
    } else if (date.contains("分") || date.contains("分钟")) {
        return 60 * time;
    }
    return time;
}

addItem("开关加载提示","加载提示");

public void 加载提示(String groupUin, String uin, int chatType) {
    if (getString("加载提示", "开关") == null) {
        putString("加载提示", "开关", "关");
        toast("已关闭加载提示");
    } else {
        putString("加载提示", "开关", null);
        toast("已开启加载提示");
    }
}

if(

getString("加载提示","开关") ==null){

toast("发送\"菜单\" 查看使用说明");
}

8. 阅读建议

如果你是开发者,建议按以下顺序阅读:

  1. 先看“开发前必读”和“脚本目录结构”。
  2. 再看“全局变量”和“回调方法”。
  3. 根据需求查阅对应 API 分类。
  4. 最后对照“菜单与交互”和“完整示例”开始编写脚本。
  5. 查看本地脚本创建示例脚本,在线脚本,搜索示例类脚本,可能会有想要的答案,

如果你是 AI 助手,建议优先提取以下信息:

  1. 运行环境限制:无注解、JDK 9、支持 Java/Android 标准类库。
  2. 编写方式:方法定义在文件根作用域,实体对象可直接按字段访问。
  3. 核心入口:onMsg、菜单回调、悬浮窗回调。
  4. 常用能力:发送消息、群操作、信息获取、存储、HTTP、文件读写。

评论 {{ comments.length }}

{{ message }}

暂无评论,来抢沙发~

回复 @{{ replyingTo.name }}

支持换行,请勿包含敏感信息。

验证码
点击加载

Powered by Anon Framework v3.4.0

© 2026 鼠子Blog