nodejs实现抖音自动关注小姐姐小哥哥神器
程序员文章站
2022-04-30 08:09:10
...
抖音自动关注小姐姐小哥哥神器
创作来源:https://github.com/wangshub/Douyin-Bot 源作者用python实现,这里用node实现一个版本,修改了一些东西
- [x] 自动翻页
- [x] 颜值检测
- [x] 人脸识别
- [x] 自动点赞
- [x] 自动关注
原理
- 打开《抖音短视频》APP,进入主界面
- 获取手机截图,并对截图进行压缩 (Size < 1MB);
- 请求 人脸识别 API;
- 解析返回的人脸 Json 信息,对人脸检测切割;
- 当颜值大于门限值
BEAUTY_THRESHOLD
时,点赞并关注; - 下一页,返回第一步;
使用教程
- 相关软件工具安装和使用步骤请参考 wechat_jump_game 和 Android 操作步骤
- 上述环境事python,这里是使用的node,所以需要在本地装一下nodejs
- 在 ai.qq.com 免费申请
AppKey
和AppID
- 获取源码:
git clone [email protected]:sunlandong/douyin_bot_node.git
- 进入源码目录:
cd douyin_bot_node
- 安装依赖:
npm install
- 运行程序:
node index.js
- 获取源码:
源码
var images = require("images");
var rp = require("request-promise");
var md5 = require("crypto-js/md5");
const url = 'https://api.ai.qq.com/fcgi-bin/face/face_detectface'
class DouyinGril {
constructor() {
const _ts = this,
{ execFile } = require('child_process');
_ts.m = {
path: require('path'),
fs: require('fs'),
os: require('os'),
chalk: require('chalk'),
execFile: execFile
};
_ts.config = {};
_ts.screenData = {};
_ts.config.os = (() => {
let osName = _ts.m.os.type();
return osName === 'Darwin' ? 'mac' : osName === 'Linux' ? 'linux' : 'win';
})();
}
async init() {
const _ts = this
try {
const v = await _ts.screenSize()
let status = v.status,
data = v.data || {};
if (status === 'success') {
_ts.screenData.width = data.width;
_ts.screenData.height = data.height;
}
} catch (e) {
_ts.log('error', e.status);
_ts.log('error', e.msg);
_ts.log('error', '请检查手机是否已经成功连接电脑并已经开启USB调试模式');
}
}
/**
* 截取手机屏幕并保存到项目的目录中
* @param {string} imgName 图片名称
*/
screencap(imgName) {
const _ts = this;
return new Promise((resolve, reject) => {
_ts.adb(`shell screencap -p /sdcard/${imgName}`).then(v => {
let savePath = _ts.m.path.join(__dirname, 'static', 'screen', imgName);
_ts.adb(`pull /sdcard/${imgName} ${savePath}`).then(v => {
resolve({
status: 'success',
msg: '完成屏幕截图更新',
data: v
});
}).catch(e => {
resolve({
status: 'success',
msg: '屏幕截图更新失败',
data: e
});
});
}).catch(e => {
reject({
status: 'error',
msg: '截图失败',
data: e
});
});
});
}
/**
* 调用api参数签名
* @param {参数} param
* @param {*} app_key
*/
getSignature(param, app_key) {
let str = Object.keys(param).sort().map(key => {
return `${key}=${encodeURIComponent(param[key])}`
})
.join("&") + "&app_key=" + app_key
return md5(str).toString().toLocaleUpperCase()
}
/**
* 改变图片尺寸
* @param {图片名称} imgName
*/
resize_image(imgName) {
const _ts = this;
let savePath = _ts.m.path.join(__dirname, 'static', 'screen', imgName);
return images(savePath).resize(1024).save(savePath, 'jpg', {
quality: 90
}).encode("jpg", {
quality: 90 //保存图片到文件,图片质量为50
})
}
/**
* 将buffer数据转成base64
* @param {buffer} data
*/
imageBufferToBase64(data) {
console.log(data)
if (Buffer.isBuffer(data)) {
return data.toString('base64')
}
}
/**
* 请求数据
*/
async getFace(image) {
let time = Number.parseInt(new Date().getTime() / 1000 + "")
let app_id = "1106868413"
let mode = 0
let time_stamp = time
let nonce_str = time + ""
let params = {
app_id: app_id,
mode: mode,
time_stamp: time_stamp,
nonce_str: nonce_str,
image: image,
sign: this.getSignature({ app_id, mode, time_stamp, nonce_str, image }, "Iisf0QhGmxD0xksG")
}
const options = {
method: 'POST',
uri: url,
form: params
};
// let redirect = 'https://jdm-test.0606.com.cn/strategy/135d630c6465f5ee3dc5b364'
let res = await rp(options)
return res
}
/**
* 开始检测
*/
async start() {
const _ts = this;
//第一步,先截图
await this.screencap("faceOriginal.png")
//处理图片
const bufferdata = this.resize_image("faceOriginal.png")
const base64 = this.imageBufferToBase64(bufferdata)
//请求数据
let res = await this.getFace(base64)
res = JSON.parse(res)
if (res.ret == 0) {
_ts.log('success', JSON.stringify(res))
let mybeauty = 0
const { face_list } = res.data
//一张图里包含多个人物
for (let index = 0; index < face_list.length; index++) {
let element = face_list[index];
let { x, y, width, height, face_id, beauty, gender } = element
images(images(_ts.m.path.join(__dirname, 'static', 'screen', "faceOriginal.png")), x, y, width, height).save(_ts.m.path.join(__dirname, 'static', 'face', face_id + ".png"))
if (beauty > 0 && gender < 50) {
//gender越接近0,越是女性,
mybeauty = beauty
if (mybeauty > 80) {
break
}
}
}
if (mybeauty > 80) {
console.log("美女哈哈哈哈")
await _ts.follow_user(500)
await _ts.thumbs_up(500)
// const screen = await this.screenSize()
// this.log('tip','屏幕的宽高'+JSON.stringify(screen))
}
}
}
/**
* 循环检测
*/
async loop() {
const _ts = this;
while (true) {
await _ts.next_page()
await _ts.start()
}
}
/**
* 获取屏幕分辩率
*/
screenSize() {
const _ts = this;
return new Promise((resolve, reject) => {
_ts.adb('shell wm size').then(v => {
if (typeof v === 'object' && typeof v.data === 'object' && typeof v.data.stdout === 'string') {
let data = v.data.stdout,
val = data.match(/\d{1,9}/ig);
resolve({
status: 'success',
msg: '屏幕分辩率获取成功',
data: {
width: val[0],
height: val[1]
}
})
} else {
reject({
status: 'error',
msg: '屏幕分辩率获取失败',
data: {}
});
};
}).catch(e => {
reject({
status: 'error',
msg: '屏幕分辩率获取出错',
data: e
});
});
});
}
/**
* adb命令
* @param {string} command adb命令字符串
* @returns {object} 返回一个Promise对象
*/
adb(command) {
const _ts = this,
m = _ts.m,
config = _ts.config;
return new Promise((resolve, reject) => {
let adbFile = (() => {
let extensionName = config.os === 'win' ? '.exe' : '';
return m.path.join(__dirname, 'tool', config.os, 'adb' + extensionName);
})();
m.execFile(adbFile, command.split(' '), null, (error, stdout, stderr) => {
if (error) {
reject({
status: 'error',
msg: `<adb ${command}> 执行错误`,
data: error
});
} else {
resolve({
status: 'success',
msg: `<adb ${command}> 执行成功`,
data: {
stdout: stdout,
stderr: stderr
}
});
};
});
});
}
/**
* 打印日志
* @param {日志类型} type
* @param {*} text
*/
log(type, text) {
const _ts = this,
m = _ts.m,
log = console.log;
switch (type) {
case 'success':
log(m.chalk.green(text));
break;
case 'error':
log(m.chalk.red(text));
break;
case 'tip':
log(m.chalk.yellow(text));
break;
default:
log(text);
break;
};
}
/**
* 暂停时间
* @param {*} timeout
*/
stopTime(timeout) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, timeout);
})
}
/**
* 翻页
*/
next_page() {
const _ts = this
return new Promise((resolve, reject) => {
let
x1 = _ts.screenData.width / 2,
y1 = _ts.screenData.height / 2 + 305,
x2 = _ts.screenData.width / 2,
y2 = _ts.screenData.height / 2+5;
_ts.adb(`shell input swipe ${x1} ${y1} ${x2} ${y2} 200`).then(async (v) => {
await _ts.stopTime(1500)
console.log(v)
resolve({
status: 'success',
msg: '翻页',
data: v
});
}).catch(async (e) => {
await _ts.stopTime(1500)
reject({
status: 'error',
msg: '翻页失败',
data: e
});
});
})
}
/**
* 关注用户
* @param {} time
*/
follow_user(time) {
const _ts = this;
return new Promise((resolve, reject) => {
let x1 = _ts.screenData.width * 0.91 ,
y1 = _ts.screenData.height / 2 * 0.98;
_ts.adb(`shell input tap ${x1} ${y1}`).then(async(v) => {
console.log(v)
await _ts.stopTime(time)
resolve({
status: 'success',
msg: '关注用户',
data: v
});
}).catch(e => {
reject({
status: 'error',
msg: '关注用户失败',
data: e
});
});
})
}
/**
* 点赞
* @param {} time
*/
thumbs_up(time) {
const _ts = this;
return new Promise((resolve, reject) => {
let x1 = _ts.screenData.width * 0.91 ,
y1 = _ts.screenData.height / 2 * 1.1;
_ts.adb(`shell input tap ${x1} ${y1}`).then(async(v) => {
console.log(v)
await _ts.stopTime(time)
resolve({
status: 'success',
msg: '点赞',
data: v
});
}).catch(e => {
reject({
status: 'error',
msg: '点赞失败',
data: e
});
});
})
}
}
start = async ()=>{
const douyin = new DouyinGril()
await douyin.init()
douyin.loop()
}
start()
依赖仓库
- “chalk”: “^2.4.1”
- “crypto-js”: “^3.1.9-1”
- “images”: “^3.0.1”
- “request”: “^2.87.0”
- “request-promise”: “^4.2.2”