Bing Rewards自动化运行

前言&摸索历程

之前看到Bing Rewards能赚一些东西,每天差不多能赚个1块多钱,但是又要求我天天使用必应搜索,而且还是手机端和电脑端都要。我肯定不能每天这么勤勤恳恳地做啊,所以我就想着能不能整个什么东西让它自己来跑。

欸,正好,我之前有一台旧电脑,CPU是i3-3220,内存有8G,让它运行这个应该很不错。系统上,我起初安装的是Windows 8.1,奈何到2024年10月,我安装的安卓模拟器没什么好使的,于是我转向了Linux。我希望用Waydroid来代替一众安卓模拟器,于是我最开始安装了KUbuntu。Waydroid在KDE上的表现并没有我预想中的这么好,会有一些Bug,比如安卓容器内的鼠标位置和外面KDE的鼠标位置不重合,会有一定的偏差。后来我换用Ubuntu,心想GNOME配Wayland使用Waydroid应该很丝滑吧,事实上我错了。Waydroid这回倒是运行得很正常,倒是我写自动化脚本的时候捉急了。很多自动化的功能在Wayland下运行不正常!甚至连鼠标的位置都没有什么好的方法获取,遂作罢。

最后,我使用了Windows 10加上WSA的组合,用下来发现这才是最顺滑的。虽然一直在说Windows屎山,但是方便也是真方便啊。经过一番折腾,便有了以下结果。

准备

这套系统需要这些东西。东西如何配置将在后面的章节说明。

  • 一台能安装Windows 10和WSA且流畅不卡顿的电脑
  • 新版Edge浏览器和WSA(安卓安装Via浏览器)
  • ADB 安卓调试桥
  • Java 21
  • C++编译器(比如VS的MSVC)
  • 电脑开机卡(或者用Wake On Lan代替)
  • 显卡欺骗器(Windows 10有软件的虚拟显示器,大家可以试试)

    预期

  1. 这套系统通过电脑开机卡,使电脑每天自动启动。你也可以通过WOL或自己每天辛苦一下开个机代替。
  2. 电脑中的Windows 10系统启动后自动启动我的Java程序,通过Java程序自动打开浏览器,完成Bing Rewards任务,并在结束任务后自动关机。

    实战


    安装系统和必要软件

首先安装Windows 10,这个应该没什么太大问题。新版Edge浏览器是后续更新后的Windows 10的默认浏览器,所以Edge不需要额外安装。

然后安装Java 21,安装Java的教程也很多,这个问题也不大。

其次是安装WSA,这个我推荐使用Github上有人制作的安装包,链接。选择自己对应的版本后下载,然后将下载的文件解压到一个文件夹,路径最好是全英文的。解压后,里面会有一个Run.bat的批处理文件。先在系统的控制面板->程序->启用或关闭Windows功能中启用虚拟机平台,然后重启Windows系统,最后运行刚才的Run.bat完成WSA的安装。

WSA安装好之后,在开始菜单中找到“适用于Android™的Windows子系统”,在那里面把开发人员选项打开,然后通过adb连接WSA,通过命令行cmd输入adb connect 127.0.0.1:58526。连接成功后,下载一个Via的安装包,然后通过命令adb install via.apk安装Via浏览器。

最后为Edge浏览器和Via浏览器安装上对应的挂机脚本,这个脚本不是我的,原作者提供了视频和教程,见B站

为Java编写JNI dll库

由于我是使用的Java来编写的,Windows API并不能直接在Java中访问,而我需要Windows API来对窗口进行一些调整。 编译后的JNI dll可以在这里下载。以下是dll的创建方法: 在VS中新建一个dll项目,然后在dllmain.cpp中这样写:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include 
#include 
#include 
#include 

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

extern "C" {

    //java函数WindowManager.moveWindow(long hwnd, int x, int y)的实现.移动一个窗口.
    JNIEXPORT void JNICALL Java_main_WindowManager_moveWindow(JNIEnv* env, jobject obj, jlong hwnd, jint x, jint y) {
        SetWindowPos((HWND)hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
    }

    //java函数WindowManager.resizeWindow(long hwnd, int width, int height)的实现.调整一个窗口的大小.
    JNIEXPORT void JNICALL Java_main_WindowManager_resizeWindow(JNIEnv* env, jobject obj, jlong hwnd, jint width, jint height) {
        SetWindowPos((HWND)hwnd, NULL, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER);
    }

    //用于C++ WindowsAPI获取所有窗口句柄时的回调函数,仅在下面Java函数WindowManager.getAllWindow()的实现中使用.
    BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
        std::vector* windowHandles = reinterpret_cast<std::vector*>(lParam);
        windowHandles->push_back(reinterpret_cast(hwnd));
        return TRUE;
    }

    //java函数WindowManager.getAllWindow()的实现.获取所有窗口的句柄并以long数组的形式返回.
    JNIEXPORT jlongArray JNICALL Java_main_WindowManager_getAllWindow(JNIEnv* env, jobject obj) {
        std::vector windowHandles;
        //通过C++ WindowsAPI获取所有窗口.
        EnumWindows(EnumWindowsProc, reinterpret_cast(&windowHandles));
        //创建Java long数组.
        jlongArray result = env->NewLongArray(windowHandles.size());
        if (result == nullptr) {
            return nullptr; //内存不足.
        }
        //将C++中的数据复制进Java数组.
        env->SetLongArrayRegion(result, 0, windowHandles.size(), windowHandles.data());
        return result;
    }

    //java函数WindowManager.getWindowTitle(long hwnd)的实现.获取窗口的标题.
    JNIEXPORT jstring JNICALL Java_main_WindowManager_getWindowTitle(JNIEnv* env, jobject obj, jlong hwnd) {
        wchar_t buffer[256];
        //获取窗口标题.
        int length = GetWindowTextW((HWND)hwnd, buffer, 256);

        if (length > 0) {
            //将C++字符串转为Java字符串.
            return env->NewString((const jchar*)buffer, length);
        }
        else {
            //获取标题失败.
            return nullptr;
        }
    }

    //java函数WindowManager.getNativeWindowPosition(long hwnd)的实现.获取窗口的位置.
    JNIEXPORT jintArray JNICALL Java_main_WindowManager_getNativeWindowPosition(JNIEnv* env, jobject obj, jlong hwnd) {
        RECT rect;
        if (GetWindowRect((HWND)hwnd, &rect)) {
            //创建Java int数组.
            jintArray result = env->NewIntArray(2);
            if (result == nullptr) {
                return nullptr; //内存不足
            }
            //设置数组内容.
            jint pos[2] = { rect.left, rect.top };
            env->SetIntArrayRegion(result, 0, 2, pos);
            return result;
        }
        else {
            //获取位置失败.
            return nullptr;
        }
    }

    //java函数WindowManager.getNativeWindowSize(long hwnd)的实现.获取窗口的大小.
    JNIEXPORT jintArray JNICALL Java_main_WindowManager_getNativeWindowSize(JNIEnv* env, jobject obj, jlong hwnd) {
        RECT rect;
        if (GetWindowRect((HWND)hwnd, &rect)) {
            //创建Java int数组.
            jintArray result = env->NewIntArray(2);
            if (result == nullptr) {
                return nullptr; //内存不足
            }
            //设置数组内容.
            jint size[2] = { rect.right - rect.left, rect.bottom - rect.top };
            env->SetIntArrayRegion(result, 0, 2, size);
            return result;
        }
        else {
            //获取大小失败.
            return nullptr;
        }
    }
    //java函数WindowManager.bringToFront(long hwnd)的实现.把窗口放到最前并聚焦(不是置顶).
    JNIEXPORT void JNICALL Java_main_WindowManager_bringToFront(JNIEnv* env, jobject obj, jlong hwnd) {
        // 获取当前线程ID
        DWORD currentThreadId = GetCurrentThreadId();
        // 获取前台窗口的线程ID
        DWORD foregroundThreadId = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
        // 如果当前线程不是前台线程,附加到前台线程
        if (currentThreadId != foregroundThreadId) {
            AttachThreadInput(currentThreadId, foregroundThreadId, TRUE);
        }
        // 将窗口带到前台
        if (!SetForegroundWindow((HWND)hwnd)) {
            //懒得解决编码问题了,这里输出使用英文(下面的都是):
            std::cerr << "Failed to bring window to front. Error: " << GetLastError() << std::endl;
        }
        // 恢复线程输入状态
        if (currentThreadId != foregroundThreadId) {
            AttachThreadInput(currentThreadId, foregroundThreadId, FALSE);
        }
    }

    //java函数WindowManager.setWindowFront(long hwnd)的实现.置顶窗口.
    JNIEXPORT void JNICALL Java_main_WindowManager_setWindowFront(JNIEnv* env, jobject obj, jlong hwnd) {
        if (!SetWindowPos((HWND)hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)) {
            std::cerr << "Failed to set window front. Error: " << GetLastError() << std::endl;
        }
    }

    //java函数WindowManager.unsetWindowFront(long hwnd)的实现.取消置顶窗口.
    JNIEXPORT void JNICALL Java_main_WindowManager_unsetWindowFront(JNIEnv* env, jobject obj, jlong hwnd) {
        if (!SetWindowPos((HWND)hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)) {
            std::cerr << "Failed to unset window front. Error: " << GetLastError() << std::endl;
        }
    }
}

在项目的属性中,在 C/C++ -> 代码生成 中设置运行库为多线程调试(/MTd),然后在 VC++目录 中的 包含目录 添加如下:

%JAVA_HOME%/include
%JAVA_HOME%/include/win32

其中%JAVA_HOME%是你安装的Java的目录,这样操作之后VS才能知道jni.h和一堆的JNI有关的东西是从哪里来的。 生成WindowManager,并保存好产生的WindowManager.dll,这就是我刚才通过网络分享的文件。

在Java中编写JNI实现

整个Java项目没有使用到任何依赖,但使用了Maven。这是Java项目的文件列表。

├─.idea   //IDEA的配置文件,不管
├─src
│   ├─main
│   │  ├─java
│   │  │  └─main
│   │  │     ├─Main.java   //java项目的主要文件,业务代码放在这里
│   │  │     ├─Robot.java   //为Main.java提供的一些方便的函数(本文的例子中里面的方法没有全部被使用)
│   │  │     └─WindowManager.java   //这一步需要编写的java代码,里面的部分函数本文并未使用到,但你或许需要这些.
│   │  └─resources
│   │     └─WindowManager.dll   //上一步中获得的dll文件,请复制到这里
│   └─test
│       └─java
├─.gitignore
└─pom.xml

这一步我们来编写WindowManager类来实现JNI方法。代码见下:

package main;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;

/**
 * 通过JNI控制Windows的窗口(不跨平台).此类中关于窗口位置和窗口大小的函数并不是精确的!
 * @author Denvo
 * @version 0.0.1
 */
public class WindowManager {
    public WindowManager() {
        //确定dll文件是否存在.
        File dllFile = new File(Main.JAR_PATH + "/WindowManager.dll");
        if (!dllFile.exists()) {
            try {
                Files.copy(WindowManager.class.getResourceAsStream("/WindowManager.dll"), dllFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        //尝试加载dll库.
        System.load(dllFile.getPath());
    }

    /**
     * 移动一个窗口到指定的位置.
     * @param hwnd 需要移动的窗口
     * @param x 移动到的目标位置
     * @param y 移动到的目标位置
     */
    public native void moveWindow(long hwnd, int x, int y);

    /**
     * 控制一个窗口的大小.
     * @param hwnd 需要控制的窗口
     * @param width 目标宽度
     * @param height 目标高度
     */
    public native void resizeWindow(long hwnd, int width, int height);

    /**
     * 获取Windows上的所有窗口.
     * @return 将所有窗口的句柄以long的类型返回
     */
    public native long[] getAllWindow();

    /**
     * 获取一个窗口的标题.
     * @param hwnd 目标窗口的句柄
     * @return 窗口标题
     */
    public native String getWindowTitle(long hwnd);

    /**
     * 获取窗口的坐标.
     * @param hwnd 目标窗口
     * @return 窗口的坐标
     */
    public int[] getWindowPosition(long hwnd) {
        int[] result = getNativeWindowPosition(hwnd);
        //由于关于坐标和大小的函数的返回值貌似不是很精确,只能通过这种方式勉强弥补一下,说白了就是不要乱动这里的+8和下面的-16.
        result[0] = result[0] + 8;
        result[1] = result[1] + 8;
        return result;
    }

    /**
     * native代码,在getWindowPosition中调用.
     * @param hwnd 窗口
     * @return 窗口位置
     */
    private native int[] getNativeWindowPosition(long hwnd);

    /**
     * 获取窗口的大小.
     * @param hwnd 目标窗口
     * @return 窗口的大小(宽度,高度)
     */
    public int[] getWindowSize(long hwnd) {
        int[] result = getNativeWindowSize(hwnd);
        //原因见getWindowPosition(long hwnd)方法.
        result[0] = result[0] -16;
        result[1] = result[1] -16;
        return result;
    }

    /**
     * native代码,在getWindowSize中调用.
     * @param hwnd 窗口
     * @return 窗口大小
     */
    private native int[] getNativeWindowSize(long hwnd);

    /**
     * 使一个窗口被放到最前端(不是置顶)并聚焦(获得焦点).
     * 能使任务栏上的图标发出橙色闪烁,提示需要查看这个窗口.
     * 只想使窗口放到最前建议使用bringWindowFront()方法.
     * @param hwnd 目标窗口
     */
    public native void bringToFront(long hwnd);

    /**
     * 置顶窗口.
     * @param hwnd 目标窗口
     */
    public native void setWindowFront(long hwnd);

    /**
     * 取消置顶窗口.
     * @param hwnd 目标窗口
     */
    public native void unsetWindowFront(long hwnd);

    /**
     * 使一个窗口被放到最前端(不是置顶)并聚焦(获得焦点).
     * 一般情况使用此方法.如需使任务栏图标闪烁提示,请使用bringToFront()方法.
     * @param hwnd 目标窗口
     */
    public void bringWindowFront(long hwnd) {
        setWindowFront(hwnd);
        try {
            Thread.sleep(10);
            unsetWindowFront(hwnd);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

编写一些方便的函数

我在Robot类中写了一些方便的函数,内容是这样的:

package main;

import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 操作鼠标和键盘自动化的类.此类中所有的方法都是单线程(注释中标注的除外),可能会导致线程堵塞!
 * @author Denvo
 * @version 0.0.1
 */
public class Robot {
    private final java.awt.Robot robot;
    {
        try {
            robot = new java.awt.Robot();
            robot.setAutoDelay(66);
        } catch (AWTException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 点击屏幕上某一个位置.
     * @param x 需要点击的位置的坐标
     * @param y 需要点击的位置的坐标
     */
    public void click(int x, int y) {
        robot.mouseMove(x, y);
        try {
            Thread.sleep(66);
            robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
            Thread.sleep(66);
            robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
            Thread.sleep(66);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过模拟键盘敲击的方式输入文字.只支持大小写字母、阿拉伯数字以及实体键盘上有的符号!
     * @param str 需要输入的文字
     */
    public void input(String str) {
        char[] chars = str.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (Character.isLowerCase(chars[i])) {
                //判断字符是否是小写字母
                pressKey(KeyEvent.getExtendedKeyCodeForChar(chars[i]));
            } else if (Character.isUpperCase(chars[i])) {
                //判断字符是否是大写字母
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.getExtendedKeyCodeForChar(Character.toLowerCase(chars[i])));
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if (Character.isDigit(chars[i])) {
                //判断字符是否为阿拉伯数字
                pressKey(KeyEvent.getExtendedKeyCodeForChar(chars[i]));
            }else if (' ' == chars[i]) {
                //判断是否是空格
                pressKey(KeyEvent.VK_SPACE);
            } else if (',' == chars[i]) {
                //后续都是各种符号的判断
                pressKey(KeyEvent.VK_COMMA);
            } else if ('.' == chars[i]) {
                pressKey(KeyEvent.VK_PERIOD);
            }else if ('/' == chars[i]) {
                pressKey(KeyEvent.VK_SLASH);
            } else if (';' == chars[i]) {
                pressKey(KeyEvent.VK_SEMICOLON);
            } else if ('\'' == chars[i]) {
                pressKey(KeyEvent.VK_QUOTE);
            } else if ('[' == chars[i]) {
                pressKey(KeyEvent.VK_OPEN_BRACKET);
            } else if (']' == chars[i]) {
                pressKey(KeyEvent.VK_CLOSE_BRACKET);
            } else if ('\\' == chars[i]) {
                pressKey(KeyEvent.VK_BACK_SLASH);
            } else if ('`' == chars[i]) {
                pressKey(KeyEvent.VK_BACK_QUOTE);
            } else if ('-' == chars[i]) {
                pressKey(KeyEvent.VK_MINUS);
            } else if ('=' == chars[i]) {
                pressKey(KeyEvent.VK_EQUALS);
            } else if ('~' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_BACK_QUOTE);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('!' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_1);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('@' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_2);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('#' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_3);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('$' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_4);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('%' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_5);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('^' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_6);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('&' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_7);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('*' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_8);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('(' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_9);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if (')' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_0);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('_' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_MINUS);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('+' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_EQUALS);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('{' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_OPEN_BRACKET);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('}' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_CLOSE_BRACKET);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('|' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_BACK_SLASH);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if (':' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_SEMICOLON);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('"' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_QUOTE);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('<' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_COMMA);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('>' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_PERIOD);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else if ('?' == chars[i]) {
                robot.keyPress(KeyEvent.VK_SHIFT);
                pressKey(KeyEvent.VK_SLASH);
                robot.keyRelease(KeyEvent.VK_SHIFT);
            } else {
                throw new RuntimeException("键盘上未找到对应的字符:" + chars[i]);
            }
        }
    }

    /**
     * 按一次键盘上特定的键.
     * @param keycode 要按下的按键的keycode
     */
    public void pressKey(int keycode) {
        robot.waitForIdle();
        robot.keyPress(keycode);
        try {
            Thread.sleep(66);
            robot.keyRelease(keycode);
            Thread.sleep(66);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 执行一段命令行.运行的命令将在新建的虚拟线程内执行,所有输出都将通过System.out.println()方法打印.
     * @param cmd 需要执行的命令行.
     * @param workDir 如果需要工作目录,则输入目录的路径;传入null则使用jar文件的目录.
     */
    public void run(String cmd, String workDir) {
        String[] cmd_slipt = cmd.split(" ");
        ProcessBuilder processBuilder;
        String charset;
        //对跨平台(Windows/Linux/MacOS)处理.虽然本程序只打算用于Windows.
        if (System.getProperty("os.name").toLowerCase().contains("win")) {
            processBuilder = new ProcessBuilder("cmd", "/c");
            charset = "GBK";
        } else {
            processBuilder = new ProcessBuilder("/bin/sh", "-c");
            charset = "UTF-8";
        }
        for (int i = 0; i < cmd_slipt.length; i++) {
            processBuilder.command().add(cmd_slipt[i]);
        }
        //对工作目录的处理.
        if (workDir == null) {
            processBuilder.directory(new File(Main.JAR_PATH));
        } else {
            processBuilder.directory(new File(workDir));
        }
        //合并输出流.
        processBuilder.redirectErrorStream(true);
        //启动进程.
        Thread.startVirtualThread(() -> {
            try {
                Process process = processBuilder.start();
                BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), charset));
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
                System.out.println("命令执行结束.退出代码为 " + process.waitFor());
                process.destroy();
            } catch (IOException | InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
    }
}

这个java项目并没有设计日志等功能,它只能勉强满足使用。

编写Java主类实现自动化

所有的自动化的步骤和逻辑在这里完成。这个每个人要根据自己的需求来改,这是我的,就是按着顺序点击各个地方,然后等待一段时间自动关机。

package main;

import java.io.File;

/**
 * AutoRunner的主类.
 * @author Denvo
 * @version 0.0.1
 */
public class Main {
    public static final String JAR_PATH = new File(Main.class.getProtectionDomain().getCodeSource().getLocation().getFile()).getParent();
    public static void main(String[] args) throws InterruptedException {
        //前面如果需要处理什么内容...

        Robot robot = new Robot();
        WindowManager wm = new WindowManager();

        Thread.sleep(3000);
        //启动电脑端edge浏览器.
        robot.run("\"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe\" cn.bing.com", null);
        Thread.sleep(8000);
        long[] hwnds = wm.getAllWindow();
        for (int i = 0; i < hwnds.length; i++) {
            if (wm.getWindowTitle(hwnds[i]) != null && wm.getWindowTitle(hwnds[i]).endsWith("Edge")) {
                wm.moveWindow(hwnds[i], 0, 0);
                Thread.sleep(10);
                wm.bringWindowFront(hwnds[i]);
                Thread.sleep(10);
                wm.resizeWindow(hwnds[i], 1424, 1039);
                Thread.sleep(10);
                wm.bringWindowFront(hwnds[i]);
                Thread.sleep(1000);
                break;
            }
        }
        robot.click(1220, 64);
        Thread.sleep(1000);
        robot.click(990, 160);
        //启动WSA的via浏览器.
        robot.run("\"C:\\Users\\Denvo\\AppData\\Local\\Microsoft\\WindowsApps\\MicrosoftCorporationII.WindowsSubsystemForAndroid_8wekyb3d8bbwe\\WsaClient.exe\" /launch wsa://mark.via", null);
        //等待1分钟进行加载.
        Thread.sleep(60000);
        hwnds = wm.getAllWindow();
        for (int i = 0; i < hwnds.length; i++) {
            if (wm.getWindowTitle(hwnds[i]) != null && wm.getWindowTitle(hwnds[i]).endsWith("Via")) {
                wm.moveWindow(hwnds[i], 1427, 6);
                Thread.sleep(10);
                wm.bringWindowFront(hwnds[i]);
                Thread.sleep(10);
                wm.resizeWindow(hwnds[i], 485, 1018);
                Thread.sleep(10);
                wm.bringWindowFront(hwnds[i]);
                Thread.sleep(1000);
                break;
            }
        }
        robot.click(1468, 70);
        Thread.sleep(1000);
        robot.click(1477, 322);
        Thread.sleep(1000);
        robot.click(1550, 493);
        Thread.sleep(1000);
        robot.click(1515, 695);
        //等待一定时间后关闭窗口并关机.
        Thread.sleep(1800000);
        robot.click(1041, 22);
        Thread.sleep(1000);
        robot.click(1889, 29);
        robot.run("shutdown -s -t 0", null);
    }
}

打包jar文件并放到目标机器

我使用了Maven,我在Maven中这么配置pom.xml:(抱歉因为一些原因没有转成代码块)

<?xml version=”1.0″ encoding=”UTF-8″?>
<project xmlns=”http://maven.apache.org/POM/4.0.0″
         xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
         xsi:schemaLocation=”http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd”>
    <modelVersion>4.0.0</modelVersion>
    <groupId>xyz.denvo</groupId>
    <artifactId>AutoRunner</artifactId>
    <version>0.0.1</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>main.Main</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>install</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

 

通过Maven的package打包项目成jar文件后,直接把这个jar文件扔到我那台i3-3220的机器上,然后编写一个启动文件(批处理文件):

java -jar ./AutoRunner-0.0.1.jar

将这个启动文件放在与jar文件同目录下,然后创建这个启动文件的快捷方式,并将快捷方式放在C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp目录下实现开机启动。

或者,使用我之前写的自动化运行批处理文件的程序(发布地址下载用法),以免启动批处理文件的黑框框。

附加选项

可以给电脑安装个ToDesk啊,向日葵啊,甚至Sunshine也行,到时候就可以通过远程查看这个系统运行是否正常。当然,不安装也是可以的。

结束

现在,重启我的i3-3220电脑,这套自动化Bing Rewards系统就启动了,我把这个挂一段时间,就可以兑换很多东西了,需求解决,nice!

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇