牙叔教程 简单易懂
autojs-robot的优势
- 不依赖任何框架, 因为根本不是用的协议
- 支持任何版本的qq
- 不会出现滑块之类的问题, 只要你的Q号没问题
autojs-robot适用场景
管理自己的Q群, 需要简单的问答机器人, 机器人发言频率不高
autojs-robot不适用场景
机器人频繁发消息, 发言频率特别快的场景, 因为通知栏监听不过来, 会漏掉消息
为啥写autojs-robot
我原来用的机器人是npm的oicq, 前几天频繁出滑块, 填写了ticket, 还是提示请用手机号登录之类的,
用手机号登录吧, 又提示
The value of "value" is out of range. It must
be >= 0 and <= 4294967295. Received 18_888_888_888
以下代码可以复现这个问题
uin = 18888888888;
const buf = Buffer.alloc(8);
buf.writeUInt32BE(uin);
寻思着换个机器人
- nenobot
- zerobot
- go-cqhttp
- ovqq
- mirai
不管那个, 对我来说, 上手成本都不低, 因此我决定用autojs写个机器人
注意
使用我这个机器人有个前提条件, 必须有两台废旧手机, 两个Q号, 如果没有, 就别往下看了
原理
- 一台手机做客户端, 监听通知栏, 监听到消息后, 提交消息给另外一台手机
- 一台手机做服务端, 接收另一台手机发送的消息, 接收到消息之后, 在qq群发消息
autojspro的优势
- autojspro支持nodejs, nodejs可以做服务端
- autojspro的两个引擎, rhino和nodejs可以通信
- 无障碍
autojspro的劣势
- 屏蔽了QQ
我的autojspro怎么能操作QQ
我的autojspro也不能操作qq呀, 我是打包以后, 用mt管理器修改了文件, 才可以操作qq的
如果不会去限制怎么办
我们的目的是机器人发消息, 那么有两种办法
- 图色
- 用二开的autojs, 比如
- Auto.js author: TonyJiangWJ
- AutoX author: kkevsekk1
- AutoJs6 author: SuperMonster003
二开的autojs要和autojspro通信的话, 可以用广播通信, 比如autox和autojspro通信,
那么就可以跳过autojspro的限制, 用autox操作所有app, 而autojspro的nodejs功能我们仍然可以使用
广播通信
发送广播
var action = "android.intent.action.牙叔教程";
app.sendBroadcast({
action: action,
extras: {
name: "牙叔教程",
dream: "One Piece",
},
});
监听广播
importPackage(android.content);
var filter = new IntentFilter();
filter.addAction("android.intent.action.牙叔教程");
var receiver = new JavaAdapter(android.content.BroadcastReceiver, {
onReceive: function (context, intent) {
var extras = intent.getExtras();
if (extras != null) {
log("name: " + extras.getString("name"));
log("dream: " + extras.getString("dream"));
}
},
});
context.registerReceiver(receiver, filter);
setInterval(() => {}, 1000);
events.on("exit", function () {
context.unregisterReceiver(receiver);
});
下面开始讲机器人的代码
监听通知栏
首先要开启通知监听
auto();
events.observeNotification();
events.onNotification(function(notification){
log(notification.getText());
});
监听到通知栏有新消息后, 我们先看看消息的具体内容
let obj = {
title: notification.getTitle(),
text: notification.text,
when: notification.when,
tickerText: notification.tickerText,
packageName: notification.getPackageName(),
};
// { title: '牙叔教程二群 (7条新消息)',
// text: '☾ 分寸 ☽: 输入法弹出还行,1秒:左右,输入:文字后缩进要8秒',
// when: 1666862029027,
// tickerText: '☾ 分寸 ☽(牙叔教程二群):输入法弹出还行,1秒左右,输入文字后缩进要8秒',
// packageName: 'com.tencent.mobileqq' }
这个通知栏的消息里面, 有qq群聊的文字内容, 有qq群名字
我们要分析这个消息是否是我们关心的消息,
我只关心我的群里的消息, 其他群不关心, 因此我先定义一个我关心的消息过滤规则
let groupMap = {
私人订制3: "xxxxxxx",
牙叔教程二群: "582976637",
牙叔教程: "zzzzzzzz",
};
let groupName = obj.title.split(" ")[0];
let groupId = groupMap[groupName];
if (groupId && groupName && message) {
// 是我关心的消息
...
}
过滤消息以后, 我们把消息发送给服务端
function postData(data) {
log("客户端发送的数据: " + JSON.stringify(data));
http.postJson(baseUrl, data, {}, function (res, err) {
if (err) {
console.error(err);
return;
}
log("code = " + res.statusCode);
log("html = " + res.body.string());
});
}
服务端
服务端这边是用autojspro实现的,
nodejs的koa监听3333端口
"nodejs";
const Koa = require("koa");
const router = require("koa-router")();
var bodyParser = require("koa-bodyparser");
/* -------------------------------------------------------------------------- */
var app = new Koa();
app.use(bodyParser());
/* -------------------------------------------------------------------------- */
router.post("/group-msg", async (ctx) => {
ctx.body = "hello world";
});
/* -------------------------------------------------------------------------- */
// 调用router.routes()来组装匹配好的路由,返回一个合并好的中间件
// 调用router.allowedMethods()获得一个中间件,当发送了不符合的请求时,会返回 `405 Method Not Allowed` 或 `501 Not Implemented`
app.use(router.routes());
app.use(router.allowedMethods({}));
app.listen(3333, () => {
console.log("koa is listening in 3333");
});
监听到消息以后, 把消息提取出来, 然后处理消息
router.post("/group-msg", async (ctx) => {
let postParam = ctx.request.body;
if (!postParam.groupId || !postParam.groupName || !postParam.message) {
ctx.response.status = 400;
ctx.body = "必须包含三个字段: groupId, groupName, message";
return;
}
let data = await handleMessage(postParam);
if (!data) {
console.log("拒绝处理空消息");
return false;
}
serverEngine.emit("reply", data);
return (ctx.body = {
code: 200,
message: "成功",
data: data,
});
});
处理消息 handleMessage 这里就是看机器人应该回复群成员什么消息,
这里就看你自己了, 你在这里的目标就是设置关键词对应的内容,
群成员触发了关键词, 你就发送对应的内容给群成员;
我在这里主要是搜索我的语雀教程, 群成员发送了关键词,
我就去语雀文档搜索对应的教程, 然后发给群成员教程链接
发送消息
最基础的无障碍操作
这是发消息的逻辑代码
function sendGroupMsg(data) {
if (!data.groupId || !data.groupName || !data.message) {
log("参数异常" + JSON.stringify(data));
return;
}
openQqGroup(data.groupId);
click发消息按钮();
enterMsg(data.message);
clickSendButton();
}
这是点击按钮的代码, 基础的无障碍操作代码我就不贴了
function clickSendButton() {
let view = text("发送")
.visibleToUser(true)
.boundsInside(0, device.height * 0.7, device.width, device.height)
.findOne(3000);
if (!view) {
throw new Error("没有找到发送按钮");
}
view.click();
}
其中最关键的代码是跳转qq群页面
function openQqGroup(groupId) {
let data = "mqqapi://card/show_pslcard?src_type=internal&version=1&card_type=group&uin=" + groupId;
app.startActivity({
action: "android.intent.action.VIEW",
data: data,
packageName: "com.tencent.mobileqq",
});
}
队列
队列的出现, 是为了存用户发送的消息, 我们处理完一个消息, 再从队列里面取下一条消息处理
监听到消息, 先把消息存起来
$events.on("reply", (data) => {
console.log("rhino 收到回复: " + JSON.stringify(data));
queue.push(data);
});
一个无限循环处理队列, 依次排序回复用户消息
threads.start(function () {
while (true) {
if (queue.length > 0) {
let data = queue.shift();
serverService.sendGroupMsg(data);
}
sleep(1000);
}
});
多个用户触发多个关键词
我不用考虑高并发, 因为我的群里没几个人发言, 就算有, 那也没问题;
我把消息存到了队列里面, 机器人会依次回复消息的;
除非同一时间多个用户发消息, 通知栏监听漏掉消息, 一般没这种情况发生
总结
大家主要理解这个机器人思路, 代码都很简单,
学习成本低, 可比那些用协议的机器人简单多了, 用那种协议框架, 啃文档成本就很大, 最烦的就是登录不进去;
现在这个autojs-robot, 只要你的q号可以在手机正常登录, 那么就不会遇到任何登录问题, 也不会发生密码泄露;
自己的机器人, 自己掌握, 不依赖任何人, 任何框架, 一个autojspro就搞定
环境
设备: 两台废旧手机, 安卓7以上(含安卓7)
autojspro: 9.2.13
名人名言
思路是最重要的, 其他的百度, bing, stackoverflow, github, 安卓文档, autojs文档, 最后才是群里问问 --- 牙叔教程
声明
部分内容来自网络 本教程仅用于学习, 禁止用于其他用途