Initial commit: import Operit protocol plugins from xiaoli-cli

This commit is contained in:
shuiyu1123 committed 2026-06-23 10:48:18 +00:00
commit c6600fc47d
52 files changed
+31492

No files matched your search

+410
View File
@@ -0,0 +1,410 @@
/*
METADATA
{
// QQ Intelligent Assistant Package
name: Experimental_qq_intelligent
display_name: {
zh: "实验性 QQ 智能助手"
en: "Experimental QQ Intelligent Assistant"
}description: { zh: "高级QQ智能助手,通过UI自动化技术实现QQ应用交互,支持消息自动回复、历史记录读取、联系人搜索与通讯等功能,为AI赋予QQ社交能力。适用于智能客服、自动回复、社交辅助等场景。", en: "Advanced QQ assistant powered by UI automation. Supports sending messages, reading chat history, searching contacts/groups, and basic communication workflows, enabling AI-driven social messaging scenarios." }
// Tools in this package
category: "Media"
tools: [
{
name: reply
description: { zh: "在当前聊天窗口输入消息并发送。一般情况下,用户想要ai帮忙发送消息时,需要ai自己去生成回复的消息,如果不确定发送的内容,请不要调用工具。只要是停留在聊天界面,就可以直接调用这个。", en: "Type and send a message in the current chat window. Usually the AI should generate the message content by itself; if you are unsure what to send, do not call this tool. As long as you are staying on a chat page, you can call it directly." }
parameters: [
{
name: message
description: { zh: "要发送的消息", en: "Message text to send." }
type: string
required: true
},
{
name: click_send
description: { zh: "是否点击发送按钮", en: "Whether to tap the Send button." }
type: boolean
required: false
}
]
},
{
name: find_user
description: { zh: "在QQ联系人或群成员中查找用户", en: "Find a user in QQ contacts or group chats." }
parameters: [
{
name: user_name
description: { zh: "搜索用户名称", en: "User name keyword to search." }
type: string
required: true
},
{
name: user_type
description: { zh: "搜索类型(contacts/groups)", en: "Search type: contacts or groups." }
type: string
required: true
}
]
},
{
name: find_and_reply
description: { zh: "查找用户并发送消息", en: "Find a user and send a message." }
parameters: [
{
name: message
description: { zh: "要发送的消息", en: "Message text to send." }
type: string
required: true
},
{
name: user_name
description: { zh: "发送目标用户名称", en: "Target user name keyword." }
type: string
required: true
},
{
name: user_type
description: { zh: "用户类型(contacts/groups)", en: "Target type: contacts or groups." }
type: string
required: true
},
{
name: click_send
description: { zh: "是否点击发送按钮", en: "Whether to tap the Send button." }
type: boolean
required: false
}
]
},
{
name: get_history
description: { zh: "获取当前聊天窗口的历史消息。如果用户要求读取聊天记录,那么可以用这个工具或者find_and_get_history。", en: "Get chat history from the current chat window. If the user asks to read chat history, you can use this tool or `find_and_get_history`." }
parameters: [
{
name: message_num
description: { zh: "获取的消息数量", en: "Number of messages to retrieve." }
type: number
required: false
}
]
},
{
name: find_and_get_history
description: { zh: "查找用户并获取聊天历史记录。一般情况下,用户想要让ai帮忙回消息的时候,需要先调用这个,得到历史记录后直接调用reply。", en: "Find a user and retrieve chat history. Typically, when the user wants the AI to reply, call this first to get context, then call `reply`." }
parameters: [
{
name: user_name
description: { zh: "搜索用户名称", en: "User name keyword to search." }
type: string
required: true
},
{
name: user_type
description: { zh: "搜索类型(contacts/groups)", en: "Search type: contacts or groups." }
type: string
required: true
},
{
name: message_num
description: { zh: "获取的消息数量", en: "Number of messages to retrieve." }
type: number
required: false
}
]
}
]
}*/
interface Array<T> {
at(index: number): T | undefined;
}
const QQIntelligent = (function () {
async function close_keyboard() {
await Tools.UI.pressKey("KEYCODE_BACK");
}
async function is_in_group() {
let page = await UINode.getCurrentPage();
return page.findAllByContentDesc('语音').findIndex(e => e.className?.includes('ImageButton') && (e.centerPoint?.y ?? 0) > 1000) === -1;
}
Array.prototype.at = function (index: number) {
if (index < 0) {
index = this.length + index;
}
return this[index];
}
async function get_history(params: { message_num: number | string }) {
const message_num = Number(params.message_num) || 10;
let page = await UINode.getCurrentPage();
//获取群名称
const chat_title = page.findById('com.tencent.mobileqq:id/ivTitleBtnLeft')?.parent?.allTexts()[0] ?? "";
// const is_group = await is_in_group();
console.log("chat_title", chat_title);
// console.log("is_group", is_group);
//先滑动到最底部
let tryMax = 0;
while (tryMax < 1) {
tryMax++;
await Tools.UI.swipe(100, 1000, 100, 200);
await Tools.System.sleep(500);
}
let messageList: { message: string, sender: string }[] = [];
let allMessages: { message: string, sender: string }[] = [];
//获取历史消息
tryMax = 0;
while (tryMax < 5) {
tryMax++;
page = await UINode.getCurrentPage();
console.log("page", page.toFormattedString!());
const list_view = page.findByClass('RecyclerView');
if (!list_view) {
return undefined;
}
// 清空临时消息列表
messageList = [];
// 获取当前可见的消息列表
for (const message of list_view.children) {
let message_text = message.allTexts().join('/');
let sender = "other";
//判断头像再左边还是右边
const avatar = message.findByClass('ImageView');
if (avatar) {
if ((avatar.centerPoint?.x ?? 0) < 300) {
sender = "other";
console.log("other", avatar.centerPoint?.x);
} else {
sender = "self";
console.log("self", avatar.centerPoint?.x);
}
} else {
sender = "self";
console.log("self");
}
messageList.push({ message: message_text, sender: sender });
}
// 将当前视图中的消息添加到总集合中,保持顺序
// 从下往上滑动时,将新获取的消息放在前面(保持旧消息在前,新消息在后的顺序)
allMessages = [...messageList, ...allMessages];
// 如果已经获取了足够多的消息,停止滚动
if (allMessages.length >= message_num) {
break;
}
await Tools.UI.swipe(100, 300, 100, 1000);
await Tools.System.sleep(500);
}
// 删除重复的消息,但保持原始顺序
const uniqueMessages: { message: string, sender: string }[] = [];
const seen = new Set();
for (const msg of allMessages) {
const key = `${msg.message}-${msg.sender}`;
if (!seen.has(key)) {
seen.add(key);
uniqueMessages.push(msg);
}
}
// 返回符合数量要求的消息列表(保持原始顺序)
return { messages: uniqueMessages.slice(0, message_num), chat_title: chat_title };
}
async function reply(params: { message: string, click_send: boolean }) {
// 提取参数
const message = params.message || "";
const click_send = params.click_send || false;
await Tools.UI.setText(message);
await Tools.System.sleep(500);
if (click_send) {
await Tools.UI.clickElement({
resourceId: "com.tencent.mobileqq:id/send_btn",
index: "0"
});
}
return true;
}
async function find_and_reply(params: { message: string, user_name: string, user_type: "contacts" | "groups", click_send: boolean }) {
// 提取参数
const message = params.message || "";
const user_name = params.user_name || "";
const user_type = params.user_type || "contacts";
const click_send = params.click_send || false;
let result = await find_user({ user_name: user_name, user_type: user_type });
if (!result) {
return false;
}
await Tools.System.sleep(1000);
return await reply({ message: message, click_send: click_send });
}
async function ensureActivity(activityName: string = "", packageName: string = "com.tencent.mobileqq", enterActivity: () => Promise<boolean> = async () => true, tryMax: number = 1) {
let android = new Android();
let activity = await Tools.UI.getPageInfo();
if (activity.activityName?.includes(activityName)) {
return true;
}
while (tryMax > 0) {
tryMax--;
await Tools.System.stopApp(packageName);
await Tools.System.sleep(2000);
await Tools.System.startApp(packageName);
// let intent = android.createIntent();
// intent.setComponent(packageName, activityName);
// await intent.start();
await Tools.System.sleep(3000); // Give some time for app to launch
if (await enterActivity()) {
activity = await Tools.UI.getPageInfo();
if (activity.activityName?.includes(activityName)) {
return true;
}
}
}
return false;
}
async function find_user(params: { user_name: string, user_type: "contacts" | "groups" }) {
// 提取参数
const user_name = params.user_name || "";
const user_type = params.user_type || "contacts";
if (!await ensureActivity("com.tencent.mobileqq.search.activity.UniteSearchActivity", "com.tencent.mobileqq", async () => {
const search_btn = (await UINode.getCurrentPage()).findByText("搜索");
if (search_btn) {
await search_btn.click();
return true;
}
return false;
})) {
return false;
}
let firstTarget: UINode | undefined = undefined;
let tryMax = 0;
while (tryMax < 2) {
tryMax++;
const search_btn = (await UINode.getCurrentPage()).findByText("搜索");
if (search_btn) {
await search_btn.click();
await Tools.System.sleep(500);
}
// 输入搜索内容
await Tools.UI.setText(user_name);
await Tools.System.sleep(3000 * tryMax);
await close_keyboard();
const currentPage = await UINode.getCurrentPage();
const searchResult = currentPage.findAllById('com.tencent.mobileqq:id/title');
let isNeedToCatch = false;
for (const child of searchResult!) {
if (isNeedToCatch) {
firstTarget = child;
break;
}
// const title = child.findById('com.tencent.mobileqq:id/title');
const title = child;
if (title) {
if (user_type == "contacts" && title.text == "联系人") {
isNeedToCatch = true;
} else if (user_type == "groups" && title.text == "群聊") {
isNeedToCatch = true;
}
}
}
if (firstTarget) {
await firstTarget.click();
return true;
}
}
return false;
}
async function find_and_get_history(params: { user_name: string, user_type: "contacts" | "groups", message_num: number }) {
const user_name = params.user_name || "";
const user_type = params.user_type || "contacts";
const message_num = params.message_num || 10;
let result = await find_user({ user_name: user_name, user_type: user_type });
if (!result) {
return undefined;
}
await Tools.System.sleep(1000);
return await get_history({ message_num: message_num });
}
async function main() {
// let result = await find_and_reply({ message: "你好你好!我是OPERIT,很高兴认识你!", user_name: "韩韩韩", user_type: "contacts", click_send: true });
// await find_user({ user_name: "Wind", user_type: "contacts" });
// await find_user({ user_name: "Dec", user_type: "groups" });
// await Tools.System.sleep(1000);
let result = await find_and_get_history({ user_name: "Dec", user_type: "groups", message_num: 20 });
console.log(result);
complete({
success: result
});
}
async function wrap_bool(func: (params: any) => Promise<boolean>, params: any, successMessage: string, failMessage: string, additionalMessage: string = "") {
if (await func(params)) {
complete({
success: true,
message: successMessage,
additionalMessage: additionalMessage
})
} else {
complete({
success: false,
message: failMessage,
additionalMessage: additionalMessage
})
}
}
async function wrap_data(func: (params: any) => Promise<any>, params: any, successMessage: string, failMessage: string, additionalMessage: string = "") {
const result = await func(params);
complete({
success: true,
message: successMessage,
additionalMessage: additionalMessage,
data: result
})
}
return {
main: main,
reply: async (params) => await wrap_bool(reply, params, "发送成功", "发送失败"),
find_user: async (params) => await wrap_bool(find_user, params, "查找成功", "查找失败,停留在界面", (await UINode.getCurrentPage()).toFormattedString!()),
find_and_reply: async (params) => await wrap_bool(find_and_reply, params, "发送成功", "发送失败,停留在界面", (await UINode.getCurrentPage()).toFormattedString!()),
get_history: async (params) => await wrap_data(get_history, params, "获取历史消息成功", "获取历史消息失败"),
find_and_get_history: async (params) => await wrap_data(find_and_get_history, params, "获取历史消息成功", "获取历史消息失败")
}
})();
//逐个导出
exports.reply = QQIntelligent.reply;
exports.find_user = QQIntelligent.find_user;
exports.find_and_reply = QQIntelligent.find_and_reply;
exports.get_history = QQIntelligent.get_history;
exports.find_and_get_history = QQIntelligent.find_and_get_history;
exports.main = QQIntelligent.main;