nodejs实现抖音自动关注小姐姐小哥哥神器

抖音自动关注小姐姐小哥哥神器

创作来源:https://github.com/wangshub/Douyin-Bot 源作者用python实现,这里用node实现一个版本,修改了一些东西

此项目git仓库

  • [x] 自动翻页
  • [x] 颜值检测
  • [x] 人脸识别
  • [x] 自动点赞
  • [x] 自动关注

原理

  • 打开《抖音短视频》APP,进入主界面
  • 获取手机截图,并对截图进行压缩 (Size < 1MB);
  • 请求 人脸识别 API
  • 解析返回的人脸 Json 信息,对人脸检测切割;
  • 当颜值大于门限值 BEAUTY_THRESHOLD时,点赞并关注;
  • 下一页,返回第一步;

使用教程

  • 相关软件工具安装和使用步骤请参考 wechat_jump_gameAndroid 操作步骤
  • 上述环境事python,这里是使用的node,所以需要在本地装一下nodejs
  • ai.qq.com 免费申请 AppKeyAppID
    1. 获取源码:git clone [email protected]:sunlandong/douyin_bot_node.git
    2. 进入源码目录: cd douyin_bot_node
    3. 安装依赖: npm install
    4. 运行程序: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()

依赖仓库

  1. “chalk”: “^2.4.1”
  2. “crypto-js”: “^3.1.9-1”
  3. “images”: “^3.0.1”
  4. “request”: “^2.87.0”
  5. “request-promise”: “^4.2.2”

猜你喜欢

转载自blog.csdn.net/s8460049/article/details/80536633