You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

18 KiB

一、思维导图

https://code.educoder.net/pq9azftxn/LittleRedBook/tree/main/auto.png

二、整体架构

通过这个脚本,我们可以实现小红书的自动打开进入随机直播间自动滑屏切换点赞关注评论以及发布帖子等功能。该脚本尤其适合那些需要频繁进行直播互动操作的人群,接下来,我们将逐步分析这段代码中的关键实现部分。


1. 初始化及权限检查

首先,我们的代码会检查应用的悬浮窗权限。如果没有权限,则会通过悬浮窗提示用户授权,并立即退出脚本,以确保之后的界面显示功能能够正常工作。

if (!floaty.checkPermission()) {
    toast("请授予悬浮窗权限");
    floaty.requestPermission();
    exit();
}

floaty.checkPermission() 用于检查悬浮窗权限;如果没有授权,则floaty.requestPermission() 会请求权限,并提示用户。


2. 创建悬浮窗组件

接下来,我们通过 floaty.window 创建一个包含按钮、滑动条、复选框和输入框的悬浮窗界面以期为用户提供丰富的控制选项这些控件允许用户开启与关闭点赞、设置点赞速度、切换评论模式、开启与关闭评论、开启与关闭自动滑屏与自动关注以及输入帖子内容等我们使用XML 结构定义界面布局,这样可以使得用户看到的界面整洁,且功能齐全。

let window = floaty.window(
    <vertical padding="8">
        <button id="toggle" text="爱猫的悬浮窗" />
        <!-- 包含控制按钮的组件 -->
    </vertical>
);

3. 悬浮窗拖动与收起功能

为增强用户体验,我们的脚本实现了悬浮窗的拖动和展开/收起功能,以便在不需要的时候收起,免得占用太多屏幕控件。window.toggle.setOnTouchListener 方法实现了拖动逻辑,通过判断按下时间来识别用户是拖动还是点击操作。

window.toggle.setOnTouchListener(function(view, event) {
    switch (event.getAction()) {
        case event.ACTION_DOWN:
            x = event.getRawX();
            y = event.getRawY();
            windowX = window.getX();
            windowY = window.getY();
            downTime = new Date().getTime();
            return true;
        case event.ACTION_MOVE:
            window.setPosition(windowX + (event.getRawX() - x), windowY + (event.getRawY() - y));
            return true;
        case event.ACTION_UP:
            if (new Date().getTime() - downTime < 200) {
                window.toggle.performClick();
            } 
            return true;
    }
    return true;
});

通过这种方式,用户可以自由拖动悬浮窗位置,并通过点击展开或收起控制面板以节省屏幕空间。


4. 自动点赞功能

点赞是直播互动中的关键操作,我们的脚本通过 window.likeButton.click 事件监听来实现点赞的开关控制,用户可以点击按钮来启动或停止点赞,并通过滑动条来调节点赞速度。

function startLiking() {
    stopLiking(); // 先停止之前的点赞定时器,防止重复
    toast("开始点赞");
    likingInterval = setInterval(() => {
        likePost();
    }, likeSpeed);
}

点赞是通过 click()来模拟点击屏幕右侧的指定位置实现的,其频率由 likeSpeed 控制,window.speedControl.setOnSeekBarChangeListener 用于监听滑动条变动,以实时调整点赞速度。


5. 评论功能:静态与动态评论

评论功能允许用户选择发送静态或者动态评论,静态评论是从我们预先设置的 staticComments 数组中随机抽取的,而动态评论则允许用户自定义输入内容(之后会重复发送用户的自定义内容), getStaticComment() 函数负责从预设数组中随机选择评论。

function getStaticComment() {
    return staticComments[Math.floor(Math.random() * staticComments.length)];
}

评论功能的逻辑由 startLiveOrComment 函数实现。该函数根据用户选择的模式(静态/动态)来生成评论,并自动输入到评论框中。


6. 自动关注和自动滑屏

我们的脚本中提供了自动关注主播和自动滑屏切换直播间的功能开启这两个功能后脚本会自动检测并点击屏幕中的“关注”按钮通过查找id和关键字同时实现如果找不到id就找关键字同时通过模拟滑动操作实现了在观看一定时间后自定义的自动切换下一个直播的功能。

// 自动关注按钮
window.follow.click(() => {
    if (!isAutoFollowing) {
        autoFollowThread = threads.start(() => {
            while (isAutoFollowing) {
                let followButton = id("fv").findOne(2000);
                if (followButton) followButton.click();
                sleep(1000);
            }
        });
    }
});

swipe(device.width / 3, device.Height / 2, device.width / 2, device.Height / 4, 100); 模拟向上滑动的功能,切换视频内容。


7. 自动发帖功能

最后,我们脚本提供了一个简易的发帖功能。用户可以发布图文或纯文字帖子,通过 createPost(content, isImagePost) 函数来实现发帖操作,函数可以模拟一系列点击操作,输入用户指定的内容,并选择添加图片或是不添加图片(根据选择的模式)并发布帖子。

function createPost(content, isImagePost) {
    click(539.5, 2338); // 定位并点击发帖按钮
    if (isImagePost) {
        click(307, 395); // 选择图片区域
        setText(content); // 输入帖子内容
        click(964.5, 173.5); // 点击发布
    }
}

isImagePost 参数决定是图文发布还是文字发布。这种模拟操作实现了简单的发帖功能,便于自动化管理发布内容。


三、完整代码解释

const staticComments = [ /* 静态评论内容数组,用于随机选择评论内容 */ ];
const deviceW = 1080; // 设备宽度可用device.width代替但该函数有时会出现返回值为0的情况
const deviceH = 2400; // 设备高度可用device.height代替但该函数有时会出现返回值为0的情况
let isLiking = false; // 控制点赞开关
let iscomment = false; // 控制评论开关
let likeSpeed = 1000; // 初始点赞速度(毫秒)
let likingInterval; // 存储点赞的定时器

// 检查悬浮窗权限,如果没有则请求权限并退出脚本
if (!floaty.checkPermission()) {
    toast("请授予悬浮窗权限");
    floaty.requestPermission();
    exit();
}

// 创建悬浮窗
let window = floaty.window(
    <vertical padding="8">
        <button id="toggle" text="爱猫的悬浮窗" />
        <vertical id="controls" visibility="gone">
            <button id="openButton" text="打开直播" />
            <button id="likeButton" text="开始点赞" />
            <text text="速度" textColor="#ffffff" />
            <seekbar id="speedControl" max="2000" progress="500" layout_weight="1" />
            <checkbox id="enableComment" text="是否发送评论" textColor="#ffffff"/>
            <checkbox id="enableStaticComment" text="使用静态评论" textColor="#ffffff"/>
            <input id="liveCommentInput" hint="在这里输入动态评论内容" />
            <button id="startLiveButton" text="发评论" />
            <button id="follow" text="关注" w="*"/>
            <button id="autoRefresh" text="自动刷" w="*"/>
            <button id="stopButton" text="停止运行" />
            <input id="postContentInput" hint="请输入发帖内容" />
            <checkbox id="isImagePost" text="图文发布" textColor="#ffffff"/>
            <button id="publishPostButton" text="发布帖子" />
        </vertical>
    </vertical>
);
setTimeout(() => {
    ui.run(() => {
        window.liveCommentInput.setHintTextColor(colors.parseColor("#ffffff")); // 设置提示文字颜色为白色
        window.postContentInput.setHintTextColor(colors.parseColor("#ffffff"));
    });
}, 500); // 延迟 500 毫秒

// 设置初始位置和拖动
window.setPosition(100, 100);
window.setSize(-2, -2);

// 监听触摸事件以实现拖动功能
window.toggle.setOnTouchListener(function(view, event) {
    switch (event.getAction()) {
        case event.ACTION_DOWN:
            x = event.getRawX();
            y = event.getRawY();
            windowX = window.getX();
            windowY = window.getY();
            downTime = new Date().getTime();
            return true;
        case event.ACTION_MOVE:
            window.setPosition(windowX + (event.getRawX() - x), windowY + (event.getRawY() - y));
            return true;
        case event.ACTION_UP:
            if (new Date().getTime() - downTime < 200) {
                window.toggle.performClick();
            } 
            return true;
    }
    return true;
});

// 切换悬浮窗的展开和收起状态
window.toggle.click(function() {
    if (window.controls.visibility === 0) {
        window.controls.visibility = 8; // 收回
        window.toggle.setText("展开");
    } else {
        window.controls.visibility = 0; // 展开
        window.toggle.setText("爱猫的悬浮窗");
    }
});

// 打开直播按钮事件
window.openButton.click(() => {
    toast("开始运行");
    threads.start(function() {
        startLiveOrSignIn(); // 在新线程中调用,避免阻塞 UI 线程
    });
});

// 停止按钮事件
window.stopButton.click(() => {
    toast("停止运行");
    window.close(); // 关闭悬浮窗
    exit(); // 停止脚本运行
});

// 点赞按钮的点击事件
window.likeButton.click(() => {
    isLiking = !isLiking; // 切换点赞状态
    if (isLiking) {
        window.likeButton.setText("停止点赞");
        startLiking(); // 开始点赞
    } else {
        window.likeButton.setText("开始点赞");
        stopLiking(); // 停止点赞
    }
});

// 发布帖子按钮事件
window.publishPostButton.click(() => {
    let postContent = window.postContentInput.text(); // 获取帖子内容
    let isImagePost = window.isImagePost.isChecked(); // 获取是否图文发布
    if (postContent) {
        threads.start(() => {
            createPost(postContent, isImagePost); // 调用发帖函数
        });
        toast("正在发布帖子...");
    } else {
        toast("请输入发帖内容!");
    }
});

// 滑动条控制点赞速度
window.speedControl.setOnSeekBarChangeListener({
    onProgressChanged: function(seekBar, progress, fromUser) {
        likeSpeed = 2500 - progress; // 调整点赞速度
        toast("当前速度: " + likeSpeed + " 毫秒");
        if (isLiking) {
            stopLiking();
            startLiking();
        }
    }
});

// 点赞功能
function startLiking() {
    stopLiking(); // 确保之前的定时器被清除,避免重复
    toast("开始点赞");
    likingInterval = setInterval(() => {
        likePost();
    }, likeSpeed);
}

// 停止点赞功能
function stopLiking() {
    toast("停止点赞");
    if (typeof likingInterval !== 'undefined') {
        clearInterval(likingInterval); // 清除定时器
        likingInterval = undefined; // 重置 likingInterval
    }
}

// 点赞操作
function likePost() {
    console.log("执行点赞操作");
    click(900, 1000);
    sleep(50);
    click(900, 1000);
    sleep(50);
}

// 保持脚本运行
setInterval(() => {}, 1000);

// 打开直播功能
function startLiveOrSignIn() {
    launchApp('小红书');
    sleep(6000);
    swipe(1080 / 2, 2400 / 4, 1080 / 2, 2400 * 3 / 4, 500);
    sleep(1000);
    click(273.5, 682);
    sleep(5000);
}

// 自动直播或评论
function startLiveOrComment(enableComment, enableStaticComment, dynamicCommentText) {
    while (true) {
        if (enableComment && !iscomment) {
            let commentBox = desc("评论输入框").clickable(true).findOne(5000);
            if (commentBox) {
                commentBox.click();
                sleep(1000);
                enableStaticComment = window.enableStaticComment.checked;
                dynamicCommentText = getDynamicCommentText();
                let commentText = enableStaticComment ? getStaticComment() : dynamicCommentText;
                input(commentText);
                sleep(500);

                let sendButton = text("发送").findOne(3000);
                if (sendButton) {
                    sendButton.click();
                    console.log("评论发送成功:" + commentText);
                } else {
                    console.log("未找到发送按钮");
                }
                sleep(3000);
            } else {
                console.log("未找到评论框,无法发送评论");
            }
        }
    }
}

// 获取静态评论
function getStaticComment() {
    return staticComments[Math.floor(Math.random() * staticComments.length)];
}

// 开始评论按钮事件
window.startLiveButton.click(() => {
    iscomment = !iscomment;
    if (iscomment) {
        window.startLiveButton.setText("开始评论");
    } else {
        window.startLiveButton.setText("停止评论");
    }
    let commentEnabled = window.enableComment.checked;
    let useStaticComment = window.enableStaticComment.checked;
    let liveCommentText = window.liveCommentInput.text();

    threads.start(() => {
        startLiveOrComment(commentEnabled, useStaticComment, liveCommentText);
    });

    if (commentEnabled && (useStaticComment || liveCommentText)) {
        toast("开始看直播并发送评论");
    } else {
        toast("开始看直播,不发送评论");
    }
});

// 动态评论输入
window.liveCommentInput.click(() => {
    dialogs.rawInput("输入动态评论内容").then(input => {
        if (input) {
            window.liveCommentInput.setText(input);
        }
    });
});

// 获取动态评论内容
function getDynamicCommentText() {
    return window.liveCommentInput.getText();
}

// 自动刷新和关注功能
let isAutoRefreshing = false;
let isAutoFollowing = false;
let autoRefreshThread;
let autoFollowThread;

// 自动关注按钮事件
window.follow.click(() => {
    if (!isAutoFollowing) {
        isAutoFollowing = true;
        autoFollowThread = threads.start(() => {
            while (isAutoFollowing) {
                let followButton = id("fv").findOne(2000);
                if (followButton) {
                    followButton.click(); // 点击关注按钮
                    console.log("已点击关注(通过ID)");
                } else {
                    // 如果通过 ID 找不到按钮,尝试通过文本查找
                    followButton = text("关注").findOne(2000);
                    if (followButton) {
                        followButton.click(); // 点击关注按钮
                        console.log("已点击关注 (通过文本)");
                    } else {
                        console.log("未找到关注按钮 (通过ID和文本)");
                    }
                }
                sleep(1000); // 等待 1 秒再进行下一次操作
            }
        });

        // 更新按钮文本
        window.follow.setText("停止关注");
    } else {
        // 如果已经在自动关注状态,则停止自动关注
        console.log("停止自动关注");

        isAutoFollowing = false;
        if (autoFollowThread) {
            autoFollowThread.interrupt(); // 停止线程
        }

        // 恢复按钮文本
        window.follow.setText("关注");
    }
});

// 自动刷按钮事件
window.autoRefresh.click(() => {
    if (!isAutoRefreshing) {
        isAutoRefreshing = true;
        console.log("开始自动刷");

        // 创建一个新线程进行自动刷操作
        autoRefreshThread = threads.start(function() {
            while (isAutoRefreshing) {
                swipe(deviceW / 3, deviceH / 2, deviceW / 2, deviceH / 4, 100); // 模拟向上滑动
                console.log("切换到下一个视频");
                sleep(5000); // 等待 5 秒再执行下一个滑动
            }
        });

        // 更新按钮文本
        window.autoRefresh.setText("停止自动刷");
    } else {
        // 如果已经在自动刷状态,则停止自动刷
        isAutoRefreshing = false;
        if (autoRefreshThread) {
            autoRefreshThread.interrupt(); // 停止线程
        }

        console.log("停止自动刷");
        window.autoRefresh.setText("自动刷"); // 恢复按钮文本
    }
});

// 发帖功能实现
function createPost(content, isImagePost) {
    click(539.5, 2338); // 点击发帖按钮位置
    sleep(3000);

    if (isImagePost) {
        console.log("图文发帖");
        click(307, 395); // 选择图片区域
        let nextBox = desc("下一步").clickable(true).findOne(3000);
        if (nextBox) {
            nextBox.click();
        }
        sleep(1500);
        click(926, 2323); // 点击继续按钮
        sleep(2000);
        click(540, 796); // 选择图片的描述框
        sleep(1000);
        setText(content); // 输入内容
        sleep(1000);
        click(964.5, 173.5); // 发布帖子
        sleep(1000);
    } else {
        console.log("仅文字发帖");
        click(341, 2336.5); // 选择文字发帖区域
        sleep(1500);
        click(374, 1088); // 点击输入框
        sleep(1500);
        setText(content); // 输入内容
        sleep(1000);
        let nextBox = desc("下一步").clickable(true).findOne(3000);
        if (nextBox) {
            nextBox.click();
        }
        sleep(9000);
        nextBox = desc("下一步").clickable(true).findOne(3000);
        nextBox.click();
        sleep(2000);
        click(675, 2271); // 发布帖子
    }
    console.log("发帖完成");
}

// 监听发帖内容输入点击事件
window.postContentInput.click(() => {
    dialogs.rawInput("请输入发帖内容").then(input => {
        if (input) {
            window.postContentInput.setText(input); // 显示输入内容
        }
    });
});