说明miraiYuQ的过验证码都需要通过App,对于小白来说还是太麻烦了。所以直接自动通过验证码,如果验证失败,再使用其自带的过验证码方式

前提

引入我的工具类,只是用了HTTP请求的工具类而已,你也可以不需要引用,使用另外的方式来请求我的接口

// grade
implementation("me.kuku:utils-fastjson:0.0.3")
// maven
<dependency>
  <groupId>me.kuku</groupId>
  <artifactId>utils-fastjson</artifactId>
  <version>0.0.3</version>
</dependency>

总的来说,就是把获取到的验证码url传给我的api,将会收到ticket

https://api.kukuqaq.com/captcha POST
参数 url -> 验证码url

YuQ-Art

使用YuHook来实现

1、新建一个类,比如我的类是me.kuku.yuq.HookCaptchaUtils,继承于HookRunnable

class HookCaptchaUtils : HookRunnable {
    override fun init(info: HookInfo) {

    }

    private val log = LoggerFactory.getLogger(HookRunnable::class.java)

    override fun preRun(method: HookMethod): Boolean {
        log.info("验证码url: ${method.paras[1]}")


        val url = method.paras[1]!! as String
        val ticket = runBlocking {
            var ticket: String? = null
            for (i in 0..3) {
                log.info("正在尝试第${i + 1}次自动过验证码~~~")
                val jsonObject = OkHttpUtils.postJson("https://api.kukuqaq.com/captcha", mapOf("url" to url))
                if (jsonObject.getInteger("code") == 200) {
                    ticket = jsonObject.getJSONObject("data").getString("ticket")
                    log.info("自动过验证码成功")
                    break
                }
            }
            ticket
        }
        if (ticket == null) {
            log.info("自动识别验证码失败,转为手动验证验证码")
            return HookCaptchaUtils().preRun(method)
        }
        //        RainUI.webListener(url)
        method.result = CommonResult(200, "Success!", mutableMapOf("ticket" to ticket))
        return true
    }

    override fun postRun(method: HookMethod) {
        println("result: ${method.result}")
    }

    override fun onError(method: HookMethod) = false

}

2、编写一个类替换掉YuQ-Art原有的过滑块方式

class HookYuQArtQQModule: HookRunnable {

    override fun preRun(method: HookMethod): Boolean {
        val yuQArtQQModule = method.paras[0] as YuQArtQQModule
        val context = yuQArtQQModule::class.java.declaredFields[0].also { it.isAccessible = true }
            .get(yuQArtQQModule) as YuContext
        context.putBean(YuQInternalFun::class.java, "", YuQInternalFunArtQQImpl())
        AppClassloader.registerBackList(
            arrayListOf(
                "javafx.",
                "org.w3c",
                "jdk.internal.",
            )
        )
        YuHook.put(
            HookItem(
                "org.artqq.util.TenCentCaptchaUtils",
                "identifyByUrl",
                "me.kuku.yuq.HookCaptchaUtils"
            )
        )
        YuHook.put(
            HookItem(
                "org.artqq.Wtlogin._Login",
                "onSuccessSendVC",
                "com.icecreamqaq.yuq.artqq.HookPhoneCap"
            )
        )
        YuHook.put(
            HookItem(
                "com.baidu.bjf.remoting.protobuf.utils.JDKCompilerHelper",
                "getJdkCompiler",
                "com.icecreamqaq.yuq.artqq.HookProto"
            )
        )
        return true
    }
}

3、在启动之前添加至Yu-Hook

fun main(args: Array<String>) {
    YuHook.put(
        HookItem(
            "com.icecreamqaq.yuq.artqq.YuQArtQQModule",
            "onLoad",
            "me.kuku.yuq.HookYuQArtQQModule"
        )
    )
    YuQArtQQStarter.start(args)
}

YuQ-Mirai

使用YuHook来实现

1、新建一个类,比如我的类是me.kuku.yuq.config.HookSliderCaptcha,继承于HookRunnable

public class HookSliderCaptcha implements HookRunnable {

    private final Logger logger = LoggerFactory.getLogger(HookRunnable.class);

    @Override
    public void init(HookInfo info) {

    }

    @Override
    public boolean preRun(HookMethod method) {
        String url = (String) method.paras[2];
        logger.info("验证码url:" + url);
        String ticket = null;
        try {
            for (int i = 0; i < 3; i++) {
                int num = i + 1;
                logger.info("正在第" + num + "次尝试识别验证码中");
                JSONObject jsonObject = OkHttpUtils.postJson("https://api.kukuqaq.com/captcha", Maps.of("url", url));
                if (jsonObject.getInteger("code") == 200) {
                    logger.info("自动识别验证码成功");
                    ticket = jsonObject.getJSONObject("data").getString("ticket");
                    logger.info("ticket:" + ticket);
                    break;
                }
            }
        } catch (Exception ignore) {
        }
        if (ticket == null) {
            logger.info("识别验证码失败,转为yuq-mirai自带的识别方式");
            return false;
        } else {
            method.result = ticket;
        }
        return true;
    }

    @Override
    public void postRun(HookMethod method) {

    }

    @Override
    public boolean onError(HookMethod method) {
        return false;
    }
}

2、在启动之前注入YuHook

fun main(args: Array<String>) {
    YuHook.put(
        HookItem(
            "net.mamoe.mirai.utils.StandardCharImageLoginSolver",
            "onSolveSliderCaptcha",
            "me.kuku.yuq.config.HookSliderCaptcha"
        )
    )
    YuQMiraiStart.start(args)
}

Mirai

自定义验证码实现

public class KuKuLoginSolver extends LoginSolver {

    private final StandardCharImageLoginSolver standardCharImageLoginSolver = new StandardCharImageLoginSolver();


    @Nullable
    @Override
    public Object onSolvePicCaptcha(@NotNull Bot bot, @NotNull byte[] bytes, @NotNull Continuation<? super String> continuation) {
        return standardCharImageLoginSolver.onSolvePicCaptcha(bot, bytes, continuation);
    }

    @Nullable
    @Override
    public Object onSolveSliderCaptcha(@NotNull Bot bot, @NotNull String url, @NotNull Continuation<? super String> continuation) {
        MiraiLogger logger = bot.getLogger();
        logger.info("验证码url:" + url);
        for (int i = 0; i < 3; i++) {
            int num = i + 1;
            logger.info("正在第" + num + "次尝试识别验证码中");
            JSONObject jsonObject = OkHttpUtils.postJson("https://api.kukuqaq.com/captcha", Maps.of("url", url));
            if (jsonObject.getInteger("code") == 200) {
                logger.info("自动识别验证码成功");
                String ticket = jsonObject.getJSONObject("data").getString("ticket");
                logger.info("ticket:" + ticket);
                return ticket;
            }
        }
        logger.info("识别验证码失败,转为mirai自带的识别方式");
        return standardCharImageLoginSolver.onSolveSliderCaptcha(bot, url, continuation);
    }

    @Nullable
    @Override
    public Object onSolveUnsafeDeviceLoginVerify(@NotNull Bot bot, @NotNull String s, @NotNull Continuation<? super String> continuation) {
        return standardCharImageLoginSolver.onSolveUnsafeDeviceLoginVerify(bot, s, continuation);
    }
}

mirai原生

替换掉验证码实现

suspend fun main(args: Array<String>) {
    // 让mirai强制走滑块验证码
    System.setProperty("mirai.slider.captcha.supported", "true");
    val newBot = BotFactory.newBot("qq号", "密码")
    val configuration = newBot.configuration
    // 把验证码实现更换为自己的
    configuration.loginSolver = KuKuLoginSolver()
    newBot.login()
}

simbot2

如果为simbot+SpringBoot,则实现MiraiBotConfigurationFactory接口,并重写getMiraiBotConfiguration方法

@Component
public class MiraiBot implements MiraiBotConfigurationFactory {
    @NotNull
    @Override
    public BotConfiguration getMiraiBotConfiguration(@NotNull BotVerifyInfo botInfo, @NotNull MiraiConfiguration simbotMiraiConfig) {
        BotConfiguration botConfiguration = simbotMiraiConfig.getBotConfiguration().invoke(botInfo.getCode());
        System.setProperty("mirai.slider.captcha.supported", "true");
        botConfiguration.setLoginSolver(new KuKuLoginSolver());
        botConfiguration.fileBasedDeviceInfo();
        return botConfiguration;
    }
}

simbot3

1、监听MiraiBotRegisteredEvent事件,替换掉验证码实现

@Service
class BotEvent {

    @Listener
    fun ss(e: MiraiBotRegisteredEvent) {
        e.bot.originalBot.configuration.loginSolver = KuKuLoginSolver()
    }

}
@Service
public class BotEvent {

    @Listener
    public void ss(MiraiBotRegisteredEvent e) {
        e.getBot().getOriginalBot().getConfiguration().setLoginSolver(new KuKuLoginSolver());
    }
    
}

2、启动之前设置环境变量

System.setProperty("mirai.slider.captcha.supported", "true")
最后修改:2022 年 05 月 24 日
如果觉得我的文章对你有用,请随意赞赏