答题模块完成;
将CxstudyTask调整为普通类, 通过initialize入口进行初始化; 调整目录结构; 细化异常处理逻辑; 添加了配置: 是否启用AI答题、是否启用无头模式; 配置文件中可选择Selenium驱动,但除firefox外均未经测试;
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -37,3 +37,4 @@ build/
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
/config.json
|
||||
/config.json.bak
|
||||
|
||||
@@ -2,7 +2,7 @@ package work.slhaf;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import work.slhaf.config.Config;
|
||||
import work.slhaf.task.cxstudy.CxstudyTask;
|
||||
import work.slhaf.task.CxstudyTask;
|
||||
import work.slhaf.util.AiUtil;
|
||||
import work.slhaf.util.OcrUtil;
|
||||
|
||||
@@ -13,9 +13,13 @@ public class Main {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException, IOException {
|
||||
Config config = Config.load();
|
||||
AiUtil.load(config);
|
||||
OcrUtil.load(config);
|
||||
CxstudyTask.run(config);
|
||||
if (config.isAiEnable()){
|
||||
AiUtil.load(config.getAiConfig());
|
||||
OcrUtil.load(config.getOcrConfig());
|
||||
}
|
||||
|
||||
CxstudyTask cxstudyTask = CxstudyTask.initialize(config);
|
||||
cxstudyTask.run();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package work.slhaf.chat;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import work.slhaf.chat.constant.Constant;
|
||||
@@ -12,7 +11,6 @@ import work.slhaf.chat.pojo.ChatResponse;
|
||||
import work.slhaf.chat.pojo.Message;
|
||||
import work.slhaf.chat.pojo.PrimaryChatResponse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@@ -28,13 +26,13 @@ public class ChatClient {
|
||||
private int temperature;
|
||||
private int max_tokens;
|
||||
|
||||
public ChatClient(String url,String apikey,String model){
|
||||
public ChatClient(String url, String apikey, String model) {
|
||||
this.url = url;
|
||||
this.apikey = apikey;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public ChatResponse runChat(List<Message> messages){
|
||||
public ChatResponse runChat(List<Message> messages) {
|
||||
HttpRequest request = HttpRequest.post(url);
|
||||
request.header("Content-Type", "application/json");
|
||||
request.header("Authorization", "Bearer " + apikey);
|
||||
@@ -48,7 +46,7 @@ public class ChatClient {
|
||||
.temperature(temperature)
|
||||
.max_tokens(max_tokens)
|
||||
.build();
|
||||
}else {
|
||||
} else {
|
||||
body = ChatBody.builder()
|
||||
.model(model)
|
||||
.messages(messages)
|
||||
@@ -57,19 +55,14 @@ public class ChatClient {
|
||||
|
||||
HttpResponse response = request.body(JSONUtil.toJsonStr(body)).execute();
|
||||
ChatResponse finalResponse;
|
||||
try {
|
||||
|
||||
PrimaryChatResponse primaryChatResponse = JSONUtil.toBean(response.body(), PrimaryChatResponse.class);
|
||||
finalResponse = ChatResponse.builder()
|
||||
.type(Constant.Response.SUCCESS)
|
||||
.message(primaryChatResponse.getChoices().get(0).getMessage().getContent())
|
||||
.usageBean(primaryChatResponse.getUsage())
|
||||
.build();
|
||||
}catch (Exception e){
|
||||
finalResponse = ChatResponse.builder()
|
||||
.type(Constant.Response.ERROR)
|
||||
.message(response.getStatus()+": "+response.body())
|
||||
.build();
|
||||
}
|
||||
|
||||
response.close();
|
||||
return finalResponse;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import lombok.Setter;
|
||||
@Getter
|
||||
@Setter
|
||||
public class AiConfig {
|
||||
private String apikey;
|
||||
private String model;
|
||||
private String baseUrl;
|
||||
private String apikey = "";
|
||||
private String model = "";
|
||||
private String baseUrl = "";
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package work.slhaf.config;
|
||||
|
||||
import cn.hutool.json.JSONConfig;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -10,11 +8,16 @@ import org.apache.commons.io.FileUtils;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
@Data
|
||||
@Slf4j
|
||||
public class Config {
|
||||
private String browser;
|
||||
private boolean aiEnable;
|
||||
private boolean headless;
|
||||
private CxstudyConfig cxstudyConfig;
|
||||
private OcrConfig ocrConfig;
|
||||
private AiConfig aiConfig;
|
||||
@@ -23,7 +26,7 @@ public class Config {
|
||||
}
|
||||
|
||||
public static Config load() throws IOException {
|
||||
File file = new File("./config.json");
|
||||
File file = new File("config.json");
|
||||
boolean configExists = checkConfigExists(file);
|
||||
|
||||
if (!configExists) {
|
||||
@@ -36,8 +39,60 @@ public class Config {
|
||||
private static void generateConfig(File file) throws IOException {
|
||||
Scanner sc = new Scanner(System.in);
|
||||
System.out.println("\r\n---------------\r\n");
|
||||
System.out.println("开始记录配置文件: ");
|
||||
Config config = new Config();
|
||||
|
||||
System.out.println("选择Selenium驱动,必须与安装的浏览器保持一致。该程序开发时使用firefox驱动,其余浏览器驱动未经过测试");
|
||||
System.out.println("可选值: edge, firefox, safari, chrome");
|
||||
List<String> browsers = new ArrayList<>();
|
||||
browsers.add("edge");
|
||||
browsers.add("firefox");
|
||||
browsers.add("chrome");
|
||||
String browser;
|
||||
System.out.print("输入你的浏览器名称: ");
|
||||
while (true) {
|
||||
browser = sc.nextLine();
|
||||
if (browsers.contains(browser)){
|
||||
config.setBrowser(browser);
|
||||
break;
|
||||
}
|
||||
System.out.println("可选值为: edge, firefox, chrome");
|
||||
System.out.print("输入你的浏览器名称: " );
|
||||
}
|
||||
|
||||
System.out.println("\r\n---------------\r\n");
|
||||
System.out.println("是否显示GUI界面?第一次运行建议选择显示,测试没有问题后,再修改配置文件为隐藏GUI界面");
|
||||
System.out.print("输入 y (启用) 或 n (禁用) : ");
|
||||
String headlessStr = "";
|
||||
boolean headless;
|
||||
while (true) {
|
||||
headlessStr = sc.nextLine();
|
||||
if (headlessStr.equals("y") || headlessStr.equals("n")){
|
||||
headless = headlessStr.equals("y");
|
||||
break;
|
||||
}
|
||||
System.out.print("输入 y (启用) 或 n (禁用) : ");
|
||||
}
|
||||
config.setHeadless(headless);
|
||||
|
||||
System.out.println("\r\n---------------\r\n");
|
||||
String aiEnableStr;
|
||||
boolean aiEnable;
|
||||
System.out.print("是否启用答题功能(y / n): ");
|
||||
while (true) {
|
||||
aiEnableStr = sc.nextLine();
|
||||
if (aiEnableStr.equals("y") || aiEnableStr.equals("n")) {
|
||||
aiEnable = aiEnableStr.equals("y");
|
||||
break;
|
||||
}
|
||||
System.out.println("请输入 y (启用) 或 n (禁用) : ");
|
||||
}
|
||||
config.setAiEnable(aiEnable);
|
||||
|
||||
AiConfig aiConfig = new AiConfig();
|
||||
System.out.println("recording ai config:");
|
||||
OcrConfig ocrConfig = new OcrConfig();
|
||||
if (aiEnable) {
|
||||
System.out.println("记录AI配置: ");
|
||||
System.out.print("apikey: ");
|
||||
aiConfig.setApikey(sc.nextLine());
|
||||
System.out.print("baseUrl: ");
|
||||
@@ -46,37 +101,36 @@ public class Config {
|
||||
aiConfig.setModel(sc.nextLine());
|
||||
|
||||
System.out.println("\r\n---------------\r\n");
|
||||
OcrConfig ocrConfig = new OcrConfig();
|
||||
System.out.println("recording ocr config:");
|
||||
System.out.println("记录OCR(用于题目识别)配置: ");
|
||||
System.out.print("accessKeyId: ");
|
||||
ocrConfig.setAccessKeyId(sc.nextLine());
|
||||
System.out.print("accessKeySecret: ");
|
||||
ocrConfig.setAccessKeySecret(sc.nextLine());
|
||||
}
|
||||
|
||||
System.out.println("\r\n---------------\r\n");
|
||||
CxstudyConfig studyConfig = new CxstudyConfig();
|
||||
System.out.println("recording study config:");
|
||||
System.out.print("phone: ");
|
||||
System.out.println("记录学习通配置: ");
|
||||
System.out.print("手机号: ");
|
||||
studyConfig.setPhone(sc.nextLine());
|
||||
System.out.print("password: ");
|
||||
System.out.print("密码: ");
|
||||
studyConfig.setPassword(sc.nextLine());
|
||||
System.out.print("target: ");
|
||||
System.out.print("目标课程: ");
|
||||
studyConfig.setTarget(sc.nextLine());
|
||||
|
||||
System.out.println("\r\n---------------\r\n");
|
||||
Config config = new Config();
|
||||
config.cxstudyConfig = studyConfig;
|
||||
config.ocrConfig = ocrConfig;
|
||||
config.aiConfig = aiConfig;
|
||||
String configStr = JSONUtil.toJsonPrettyStr(config);
|
||||
System.out.println("this is your config file: \r\n" + configStr);
|
||||
|
||||
System.out.println("press ENTER to generate config: ");
|
||||
System.out.println("配置文件如下: \r\n" + configStr);
|
||||
System.out.println("配置文件将位于: "+file.getAbsolutePath());
|
||||
System.out.println("输入 ENTER 以创建配置文件: ");
|
||||
sc.nextLine();
|
||||
|
||||
file.createNewFile();
|
||||
FileUtils.writeStringToFile(file, configStr, StandardCharsets.UTF_8);
|
||||
System.out.println(" config generated");
|
||||
System.out.println("配置文件生成完毕");
|
||||
}
|
||||
|
||||
private static Config loadFromFile(File file) throws IOException {
|
||||
|
||||
@@ -6,9 +6,9 @@ import lombok.Setter;
|
||||
@Getter
|
||||
@Setter
|
||||
public class CxstudyConfig {
|
||||
private String phone;
|
||||
private String password;
|
||||
private String target;
|
||||
private String phone = "";
|
||||
private String password = "";
|
||||
private String target = "";
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -6,8 +6,8 @@ import lombok.Setter;
|
||||
@Getter
|
||||
@Setter
|
||||
public class OcrConfig {
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
private String accessKeyId = "";
|
||||
private String accessKeySecret = "";
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package work.slhaf.exception;
|
||||
|
||||
public class AiRequestException extends RuntimeException {
|
||||
public AiRequestException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package work.slhaf.exception;
|
||||
|
||||
public class AiResponseFormatException extends RuntimeException {
|
||||
public AiResponseFormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package work.slhaf.exception;
|
||||
|
||||
public class OcrRequestException extends RuntimeException {
|
||||
public OcrRequestException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
4
src/main/java/work/slhaf/pojo/answer/Answer.java
Normal file
4
src/main/java/work/slhaf/pojo/answer/Answer.java
Normal file
@@ -0,0 +1,4 @@
|
||||
package work.slhaf.pojo.answer;
|
||||
|
||||
public class Answer {
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package work.slhaf.task.cxstudy.pojo.answer;
|
||||
package work.slhaf.pojo.answer;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -1,4 +1,4 @@
|
||||
package work.slhaf.task.cxstudy.pojo.answer;
|
||||
package work.slhaf.pojo.answer;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -1,4 +1,4 @@
|
||||
package work.slhaf.task.cxstudy.pojo.answer;
|
||||
package work.slhaf.pojo.answer;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -1,4 +1,4 @@
|
||||
package work.slhaf.task.cxstudy.pojo.question;
|
||||
package work.slhaf.pojo.question;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -1,4 +1,4 @@
|
||||
package work.slhaf.task.cxstudy.pojo.question;
|
||||
package work.slhaf.pojo.question;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package work.slhaf.task.cxstudy.pojo.question;
|
||||
package work.slhaf.pojo.question;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -1,4 +1,4 @@
|
||||
package work.slhaf.task.cxstudy.pojo.question;
|
||||
package work.slhaf.pojo.question;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
457
src/main/java/work/slhaf/task/CxstudyTask.java
Normal file
457
src/main/java/work/slhaf/task/CxstudyTask.java
Normal file
@@ -0,0 +1,457 @@
|
||||
package work.slhaf.task;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import io.github.bonigarcia.wdm.WebDriverManager;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.openqa.selenium.*;
|
||||
import org.openqa.selenium.chrome.ChromeDriver;
|
||||
import org.openqa.selenium.chrome.ChromeOptions;
|
||||
import org.openqa.selenium.edge.EdgeDriver;
|
||||
import org.openqa.selenium.edge.EdgeOptions;
|
||||
import org.openqa.selenium.firefox.FirefoxDriver;
|
||||
import org.openqa.selenium.firefox.FirefoxOptions;
|
||||
import org.openqa.selenium.interactions.Actions;
|
||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||
import org.openqa.selenium.support.ui.WebDriverWait;
|
||||
import work.slhaf.config.Config;
|
||||
import work.slhaf.exception.AiRequestException;
|
||||
import work.slhaf.exception.AiResponseFormatException;
|
||||
import work.slhaf.exception.OcrRequestException;
|
||||
import work.slhaf.pojo.answer.Answer;
|
||||
import work.slhaf.pojo.answer.MultiChoiceAnswer;
|
||||
import work.slhaf.pojo.answer.SingleChoiceAnswer;
|
||||
import work.slhaf.pojo.answer.TextAnswer;
|
||||
import work.slhaf.pojo.question.MultiChoiceQuestion;
|
||||
import work.slhaf.pojo.question.Question;
|
||||
import work.slhaf.pojo.question.SingleChoiceQuestion;
|
||||
import work.slhaf.pojo.question.TextQuestion;
|
||||
import work.slhaf.util.AiUtil;
|
||||
import work.slhaf.util.OcrUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@Setter
|
||||
public class CxstudyTask {
|
||||
|
||||
private WebDriver driver;
|
||||
private Actions actions;
|
||||
private WebDriverWait wait;
|
||||
private JavascriptExecutor executor;
|
||||
private String phone;
|
||||
private String passwd;
|
||||
private String targetClassName;
|
||||
private boolean aiEnable;
|
||||
|
||||
private CxstudyTask() {
|
||||
}
|
||||
|
||||
public void run() throws InterruptedException {
|
||||
|
||||
login();
|
||||
while (true) {
|
||||
try {
|
||||
switchPage();
|
||||
//登录、跳转页面
|
||||
Thread.sleep(2000);
|
||||
List<WebElement> toBeFinished = getTodoTasks();
|
||||
int chapterCount = 0;
|
||||
while (!toBeFinished.isEmpty()) {
|
||||
//记录元素对应文本
|
||||
toBeFinished.getFirst().click();
|
||||
chapterCount++;
|
||||
log.info("处理第{}节", chapterCount);
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
|
||||
int subTasksNum = driver.findElements(By.xpath(".//div[@class='ans-attach-ct']")).size();
|
||||
log.info("发现{}个任务", subTasksNum);
|
||||
for (int i = 0; i < subTasksNum; i++) {
|
||||
//检测frame内部元素
|
||||
//frame分为三层:
|
||||
// 第一层:index: 6 保存所有含有内容的frame
|
||||
// 第二层:index: i 视频层 i->prev_video_left
|
||||
// 第三层:index: 0->newTestTitle, 2->fileBox
|
||||
handleVideoTask(i);
|
||||
if (aiEnable) {
|
||||
handleTestTask(i);
|
||||
}
|
||||
handleDocTask(i);
|
||||
|
||||
}
|
||||
|
||||
driver.get(driver.getCurrentUrl());
|
||||
driver.switchTo().defaultContent();
|
||||
toBeFinished = getTodoTasks();
|
||||
}
|
||||
|
||||
log.info("作答完毕!");
|
||||
driver.quit();
|
||||
break;
|
||||
}catch (AiRequestException | OcrRequestException e) {
|
||||
log.error(e.getMessage());
|
||||
driver.quit();
|
||||
break;
|
||||
}catch (WebDriverException e) {
|
||||
log.error("\r\n---------------\r\n");
|
||||
log.error(e.getLocalizedMessage());
|
||||
log.error("\r\n---------------\r\n");
|
||||
log.error("Something went wrong, will retry later..");
|
||||
driver.get("https://i.chaoxing.com/");
|
||||
Thread.sleep(5000);
|
||||
switchPage();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void handleDocTask(int i) throws InterruptedException {
|
||||
try {
|
||||
driver.switchTo().frame(i).switchTo().frame(2);
|
||||
log.info("开始处理文档浏览...");
|
||||
long lastHeight = (long) executor.executeScript("return document.body.scrollHeight;");
|
||||
long currentHeight = 0;
|
||||
int step = 2000;
|
||||
while (currentHeight < lastHeight) {
|
||||
executor.executeScript("window.scrollBy(0, arguments[0]);", step);
|
||||
Thread.sleep(600);
|
||||
currentHeight = (long) executor.executeScript("return window.scrollY + window.innerHeight;");
|
||||
lastHeight = (long) executor.executeScript("return document.body.scrollHeight;");
|
||||
}
|
||||
log.info("文档浏览任务完成!");
|
||||
} catch (WebDriverException ignored) {
|
||||
} finally {
|
||||
driver.switchTo().defaultContent();
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void handleTestTask(int i) {
|
||||
try {
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(i));
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(0));
|
||||
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//div[@class='singleQuesId']")));
|
||||
log.info("开始处理测验...");
|
||||
//所有题目元素
|
||||
List<WebElement> questions = driver.findElements(By.xpath(".//div[@class='singleQuesId']"));
|
||||
log.info("发现{}个题目...", questions.size());
|
||||
for (WebElement question : questions) {
|
||||
log.info("处理第{}个题目...", questions.indexOf(question));
|
||||
//获取题目截图,通过OCR获取题目信息
|
||||
WebElement questionTitleElement = question.findElement(By.xpath(".//div[@class='TiMu newTiMu']"));
|
||||
File screenshot = questionTitleElement.getScreenshotAs(OutputType.FILE);
|
||||
|
||||
String questionTitle = OcrUtil.getContentOfImage(screenshot);
|
||||
log.info("题目信息: {}", questionTitle);
|
||||
try {
|
||||
//尝试获取单选选项
|
||||
//li role='radio'
|
||||
question.findElement(By.xpath(".//li[@role='radio']"));
|
||||
handleSingleChoiceQuestion(question, questionTitle);
|
||||
continue;
|
||||
} catch (WebDriverException ignored) {
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
try {
|
||||
//尝试获取复选框
|
||||
//li role='checkbox'
|
||||
question.findElement(By.xpath(".//li[@role='checkbox']"));
|
||||
handleMultiChoiceQuestion(question, questionTitle);
|
||||
continue;
|
||||
} catch (WebDriverException ignored) {
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
try {
|
||||
//尝试跳转到简答题输入框
|
||||
driver.switchTo().frame("ueditor_0");
|
||||
|
||||
TextQuestion textQuestion = new TextQuestion();
|
||||
textQuestion.setTitle(questionTitle);
|
||||
Answer answer = getAnswer(textQuestion);
|
||||
TextAnswer textAnswer = (TextAnswer) answer;
|
||||
WebElement inputArea = driver.findElement(By.xpath(".//body[@contenteditable='true']"));
|
||||
//输入答案,并检测是否正确输入
|
||||
while (Objects.equals(inputArea.getText(), "")) {
|
||||
inputArea.click();
|
||||
inputArea.sendKeys(textAnswer.getAnswer());
|
||||
}
|
||||
driver.switchTo().parentFrame();
|
||||
driver.switchTo().parentFrame();
|
||||
} catch (WebDriverException ignored) {
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
log.info("答题任务完成!");
|
||||
//点击提交按钮,等待并点击弹窗
|
||||
driver.findElement(By.xpath(".//a[@class='btnSubmit workBtnIndex']")).click();
|
||||
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//a[@id='popok']"))).click();
|
||||
|
||||
} catch (WebDriverException ignored) {
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
driver.switchTo().defaultContent();
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMultiChoiceQuestion(WebElement question, String questionTitle) throws InterruptedException {
|
||||
List<WebElement> choiceElements = question.findElements(By.xpath(".//li[@role='checkbox']"));
|
||||
MultiChoiceQuestion multiChoiceQuestion = new MultiChoiceQuestion();
|
||||
multiChoiceQuestion.setTitle(questionTitle);
|
||||
List<String> choices = getChoices(choiceElements);
|
||||
multiChoiceQuestion.setChoices(choices);
|
||||
Answer answer = getAnswer(multiChoiceQuestion);
|
||||
MultiChoiceAnswer multiChoiceAnswer = (MultiChoiceAnswer) answer;
|
||||
List<String> answers = multiChoiceAnswer.getAnswers();
|
||||
//输入答案,并检测是否正确输入
|
||||
String script = getCheckScript(question);
|
||||
while (Objects.equals(executor.executeScript(script), "")) {
|
||||
for (String answerContent : answers) {
|
||||
log.info("答案: {}", answerContent);
|
||||
String xpath = ".//span[@data='" + answerContent + "']";
|
||||
WebElement clickElement = question.findElement(By.xpath(xpath)).findElement(By.xpath("..")).findElement(By.xpath(".."));
|
||||
String check = clickElement.getDomAttribute("aria-checked");
|
||||
// question.findElement(By.xpath(xpath)).findElement(By.xpath("..")).findElement(By.xpath("..")).click();
|
||||
while (check == null || check.equals("false")) {
|
||||
clickElement.click();
|
||||
check = clickElement.getDomAttribute("aria-checked");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSingleChoiceQuestion(WebElement question, String questionTitle) throws InterruptedException {
|
||||
List<WebElement> choiceElements = question.findElements(By.xpath(".//li[@role='radio']"));
|
||||
SingleChoiceQuestion singleChoiceQuestion = new SingleChoiceQuestion();
|
||||
singleChoiceQuestion.setTitle(questionTitle);
|
||||
List<String> choices = getChoices(choiceElements);
|
||||
singleChoiceQuestion.setChoices(choices);
|
||||
Answer primaryAnswer = getAnswer(singleChoiceQuestion);
|
||||
SingleChoiceAnswer answer = (SingleChoiceAnswer) primaryAnswer;
|
||||
String answerContent = answer.getAnswer();
|
||||
log.info("答案: {}", answerContent);
|
||||
if (choices.size() == 2) {
|
||||
answerContent = answerContent.equals("A") ? "true" : "false";
|
||||
}
|
||||
String xpath = ".//span[@data='" + answerContent + "']";
|
||||
//输入答案,并检测是否正确输入
|
||||
String script = getCheckScript(question);
|
||||
while (Objects.equals(executor.executeScript(script), "")) {
|
||||
question.findElement(By.xpath(xpath)).findElement(By.xpath("..")).findElement(By.xpath("..")).click();
|
||||
}
|
||||
}
|
||||
|
||||
private String getCheckScript(WebElement question) {
|
||||
WebElement finalInputElement = question.findElement(By.xpath(".//input[@type='hidden']"));
|
||||
String id = finalInputElement.getDomAttribute("id");
|
||||
return "return document.getElementById('" + id + "').value;";
|
||||
}
|
||||
|
||||
private List<String> getChoices(List<WebElement> choiceElements) {
|
||||
List<String> choices = new ArrayList<>();
|
||||
for (WebElement choiceElement : choiceElements) {
|
||||
String label = choiceElement.findElement(By.tagName("label")).getText();
|
||||
String content = choiceElement.findElement(By.tagName("a")).getText();
|
||||
choices.add(String.format("%s. %s", label, content));
|
||||
}
|
||||
return choices;
|
||||
}
|
||||
|
||||
private Answer getAnswer(Question question) throws InterruptedException {
|
||||
Answer answer = null;
|
||||
int tryTimes = 0;
|
||||
while (tryTimes < 3) {
|
||||
try {
|
||||
String questionStr = JSONUtil.toJsonPrettyStr(question);
|
||||
String responseStr = AiUtil.runChat(questionStr);
|
||||
// 使用正则表达式提取Markdown代码块中的JSON内容
|
||||
String jsonPattern = "```[\\s\\S]*?\\n([\\s\\S]*?)\\n```";
|
||||
Pattern pattern = Pattern.compile(jsonPattern);
|
||||
Matcher matcher = pattern.matcher(responseStr);
|
||||
String jsonStr = "";
|
||||
if (matcher.find()) {
|
||||
jsonStr = matcher.group(1).trim();
|
||||
}
|
||||
if (jsonStr.isEmpty()) {
|
||||
throw new AiResponseFormatException("AI 回答格式不匹配: " + jsonStr);
|
||||
}
|
||||
if (question instanceof SingleChoiceQuestion) {
|
||||
answer = JSONUtil.toBean(jsonStr, SingleChoiceAnswer.class);
|
||||
} else if (question instanceof MultiChoiceQuestion) {
|
||||
answer = JSONUtil.toBean(jsonStr, MultiChoiceAnswer.class);
|
||||
} else if (question instanceof TextQuestion) {
|
||||
answer = JSONUtil.toBean(jsonStr, TextAnswer.class);
|
||||
}
|
||||
break;
|
||||
} catch (AiResponseFormatException e) {
|
||||
log.warn(e.getLocalizedMessage());
|
||||
log.warn("3s后重试...");
|
||||
Thread.sleep(3000);
|
||||
tryTimes++;
|
||||
}
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
private void handleVideoTask(int i) throws InterruptedException {
|
||||
try {
|
||||
driver.switchTo().frame(i);
|
||||
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//div[@class='prev_video_left']")));
|
||||
log.info("开始处理视频播放...");
|
||||
WebElement play = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//button[@class='vjs-big-play-button']")));
|
||||
Thread.sleep(2000);
|
||||
play.click();
|
||||
WebElement video = driver.findElement(By.id("video_html5_api"));
|
||||
//获取总时长
|
||||
String totalTime = driver.findElement(By.xpath("//span[@class='vjs-duration-display']")).getText();
|
||||
// 获取视频的位置信息
|
||||
Point location = video.getLocation();
|
||||
Dimension size = video.getSize();
|
||||
|
||||
int videoX = location.getX();
|
||||
int videoY = location.getY();
|
||||
int videoWidth = size.getWidth();
|
||||
int videoHeight = size.getHeight();
|
||||
|
||||
// 确定鼠标起始位置:进入视频区域
|
||||
int startX = videoX + videoWidth / 4;
|
||||
int startY = videoY + videoHeight / 4;
|
||||
actions.moveToElement(video, startX, startY).perform();
|
||||
Thread.sleep(500);
|
||||
|
||||
int step = 50; // 每次移动步长
|
||||
|
||||
while (true) {
|
||||
for (int j = 0; j < 20; j++) {
|
||||
int offsetX = (int) (Math.random() * step) - step / 2; // 随机左右移动
|
||||
int offsetY = (int) (Math.random() * step) - step / 2; // 随机上下移动
|
||||
try {
|
||||
actions.moveByOffset(offsetX, offsetY).perform();
|
||||
}catch (Exception e) {
|
||||
actions.moveToElement(video, startX, startY).perform();
|
||||
}
|
||||
Thread.sleep(200);
|
||||
}
|
||||
String currentTime = driver.findElement(By.xpath(".//span[@class='vjs-current-time-display']")).getText();
|
||||
if (currentTime.isEmpty()){
|
||||
actions.moveToElement(video, startX, startY).perform();
|
||||
currentTime = driver.findElement(By.xpath(".//span[@class='vjs-current-time-display']")).getText();
|
||||
}
|
||||
if (totalTime.equals("0:00") || totalTime.isEmpty()) {
|
||||
totalTime = driver.findElement(By.xpath("//span[@class='vjs-duration-display']")).getText();
|
||||
} else if (currentTime.equals(totalTime)) {
|
||||
break;
|
||||
}
|
||||
log.info(currentTime + "/" + totalTime);
|
||||
//恢复暂停
|
||||
try {
|
||||
WebElement playingButton = driver.findElement(By.xpath(".//button[@class='vjs-play-control vjs-control vjs-button vjs-paused']"));
|
||||
playingButton.click();
|
||||
} catch (WebDriverException ignored) {
|
||||
}
|
||||
}
|
||||
log.info("视频播放任务完成!");
|
||||
} catch (WebDriverException ignored) {
|
||||
} finally {
|
||||
driver.switchTo().defaultContent();
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
|
||||
}
|
||||
}
|
||||
|
||||
private List<WebElement> getTodoTasks() {
|
||||
driver.switchTo().defaultContent();
|
||||
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div[@class='posCatalog_select']")));
|
||||
List<WebElement> toBeFinished = new ArrayList<>();
|
||||
for (WebElement webElement : driver.findElements(By.xpath("//div[@class='posCatalog_select']"))) {
|
||||
try {
|
||||
webElement.findElement(By.xpath(".//span[@class='orangeNew']"));
|
||||
toBeFinished.add(webElement);
|
||||
} catch (WebDriverException ignored) {
|
||||
}
|
||||
}
|
||||
log.info("获取到{}个任务", toBeFinished.size());
|
||||
return toBeFinished;
|
||||
}
|
||||
|
||||
private void login() {
|
||||
log.info("登录中...");
|
||||
driver.get("https://i.chaoxing.com/");
|
||||
WebElement phoneInput = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//input[@id='phone']")));
|
||||
// WebElement phoneInput = driver.findElement();
|
||||
phoneInput.sendKeys(phone);
|
||||
WebElement passwdInput = driver.findElement(By.xpath("//input[@id='pwd']"));
|
||||
passwdInput.sendKeys(passwd);
|
||||
WebElement submitInput = driver.findElement(By.xpath("//button[@id='loginBtn']"));
|
||||
submitInput.click();
|
||||
log.info("登录成功!");
|
||||
}
|
||||
|
||||
private void switchPage() throws InterruptedException {
|
||||
log.info("跳转中...");
|
||||
System.out.println(driver.getTitle());
|
||||
Thread.sleep(5);
|
||||
try {
|
||||
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div[@name='课程']"))).click();
|
||||
}catch (TimeoutException e){
|
||||
driver.findElement(By.xpath("//div[@name='课程']")).click();
|
||||
}
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(0));
|
||||
String xpath = ".//span[@title='" + targetClassName + "']";
|
||||
String targetClassHref = wait.until(ExpectedConditions.elementToBeClickable(By.xpath(xpath))).findElement(By.xpath("..")).getDomAttribute("href");
|
||||
driver.get(targetClassHref);
|
||||
|
||||
driver.findElement(By.xpath("//a[@title='章节']")).click();
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(0));
|
||||
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//div[@role='link']"))).findElement(By.xpath("..")).click();
|
||||
}
|
||||
|
||||
public static CxstudyTask initialize(Config config) {
|
||||
log.info("任务创建中...");
|
||||
|
||||
WebDriverManager.firefoxdriver().clearDriverCache().setup();
|
||||
WebDriver driver = switch (config.getBrowser()) {
|
||||
case "firefox" -> config.isHeadless() ? new FirefoxDriver(new FirefoxOptions().addArguments("--headless")) : new FirefoxDriver();
|
||||
case "edge" ->config.isHeadless() ? new EdgeDriver(new EdgeOptions().addArguments("--headless")) : new EdgeDriver();
|
||||
case "chrome" ->config.isHeadless() ? new ChromeDriver(new ChromeOptions().addArguments("--headless")) : new ChromeDriver();
|
||||
default -> throw new IllegalStateException("不支持的浏览器驱动!: " + config.getBrowser());
|
||||
};
|
||||
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));
|
||||
Actions actions = new Actions(driver);
|
||||
JavascriptExecutor executor = (JavascriptExecutor) driver;
|
||||
|
||||
CxstudyTask cxstudyTask = getCxstudyTask(config, driver, wait, actions, executor);
|
||||
|
||||
log.info("任务创建完毕!");
|
||||
return cxstudyTask;
|
||||
}
|
||||
|
||||
private static CxstudyTask getCxstudyTask(Config config, WebDriver driver, WebDriverWait wait, Actions actions, JavascriptExecutor executor) {
|
||||
CxstudyTask cxstudyTask = new CxstudyTask();
|
||||
cxstudyTask.setDriver(driver);
|
||||
cxstudyTask.setWait(wait);
|
||||
cxstudyTask.setActions(actions);
|
||||
cxstudyTask.setExecutor(executor);
|
||||
|
||||
cxstudyTask.setPhone(config.getCxstudyConfig().getPhone());
|
||||
cxstudyTask.setPasswd(config.getCxstudyConfig().getPassword());
|
||||
cxstudyTask.setTargetClassName(config.getCxstudyConfig().getTarget());
|
||||
|
||||
cxstudyTask.setAiEnable(config.isAiEnable());
|
||||
return cxstudyTask;
|
||||
}
|
||||
}
|
||||
@@ -1,334 +0,0 @@
|
||||
package work.slhaf.task.cxstudy;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import io.github.bonigarcia.wdm.WebDriverManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.openqa.selenium.*;
|
||||
import org.openqa.selenium.firefox.FirefoxDriver;
|
||||
import org.openqa.selenium.interactions.Actions;
|
||||
import org.openqa.selenium.support.ui.ExpectedConditions;
|
||||
import org.openqa.selenium.support.ui.WebDriverWait;
|
||||
import work.slhaf.config.Config;
|
||||
import work.slhaf.task.cxstudy.pojo.answer.Answer;
|
||||
import work.slhaf.task.cxstudy.pojo.answer.MultiChoiceAnswer;
|
||||
import work.slhaf.task.cxstudy.pojo.answer.SingleChoiceAnswer;
|
||||
import work.slhaf.task.cxstudy.pojo.answer.TextAnswer;
|
||||
import work.slhaf.task.cxstudy.pojo.question.MultiChoiceQuestion;
|
||||
import work.slhaf.task.cxstudy.pojo.question.SingleChoiceQuestion;
|
||||
import work.slhaf.task.cxstudy.pojo.question.Question;
|
||||
import work.slhaf.task.cxstudy.pojo.question.TextQuestion;
|
||||
import work.slhaf.util.AiUtil;
|
||||
import work.slhaf.util.OcrUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Slf4j
|
||||
public class CxstudyTask {
|
||||
|
||||
static WebDriver driver;
|
||||
static Actions actions;
|
||||
static WebDriverWait wait;
|
||||
|
||||
public static void run(Config config) throws InterruptedException {
|
||||
|
||||
String phone = config.getCxstudyConfig().getPhone();
|
||||
String passwd = config.getCxstudyConfig().getPassword();
|
||||
String targetClassName = config.getCxstudyConfig().getTarget();
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
//注册driver
|
||||
initialize();
|
||||
|
||||
//登录、跳转页面
|
||||
loginAndSwitch(phone, passwd, targetClassName);
|
||||
Thread.sleep(2000);
|
||||
|
||||
List<WebElement> toBeFinished = getTodoTasks();
|
||||
int chapterCount = 0;
|
||||
while (!toBeFinished.isEmpty()) {
|
||||
//记录元素对应文本
|
||||
toBeFinished.getFirst().click();
|
||||
chapterCount++;
|
||||
log.info("处理第{}节", chapterCount);
|
||||
// toBeFinished.get(14).click();
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
|
||||
int subTasksNum = driver.findElements(By.xpath(".//div[@class='ans-attach-ct']")).size();
|
||||
for (int i = 0; i < subTasksNum; i++) {
|
||||
//检测frame内部元素
|
||||
//frame分为三层:
|
||||
// 第一层:index: 6 保存所有含有内容的frame
|
||||
// 第二层:index: i 视频层 i->prev_video_left
|
||||
// 第三层:index: 0->newTestTitle, 2->fileBox
|
||||
// int taskContainersNum = driver.findElements(By.xpath(".//div[@class='ans-attach-ct']")).size();
|
||||
// log.info("发现{}个任务", taskContainersNum);
|
||||
// for (int t = 0; t < taskContainersNum; t++) {
|
||||
// handleVideoTask(i);
|
||||
handleTestTask(i);
|
||||
// handleDocTask(i);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
driver.get(driver.getCurrentUrl());
|
||||
driver.switchTo().defaultContent();
|
||||
toBeFinished = getTodoTasks();
|
||||
}
|
||||
|
||||
driver.quit();
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
log.error("\r\n---------------\r\n");
|
||||
log.error(e.getLocalizedMessage());
|
||||
log.error("\r\n---------------\r\n");
|
||||
driver.quit();
|
||||
log.error("Something went wrong, will retry later..");
|
||||
Thread.sleep(5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void handleDocTask(int i) {
|
||||
try {
|
||||
driver.switchTo().frame(i).switchTo().frame(2);
|
||||
log.info("开始处理文档浏览...");
|
||||
JavascriptExecutor js = (JavascriptExecutor) driver;
|
||||
long lastHeight = (long) js.executeScript("return document.body.scrollHeight;");
|
||||
long currentHeight = 0;
|
||||
int step = 2000;
|
||||
while (currentHeight < lastHeight) {
|
||||
js.executeScript("window.scrollBy(0, arguments[0]);", step);
|
||||
Thread.sleep(600);
|
||||
currentHeight = (long) js.executeScript("return window.scrollY + window.innerHeight;");
|
||||
lastHeight = (long) js.executeScript("return document.body.scrollHeight;");
|
||||
}
|
||||
log.info("文档浏览任务完成!");
|
||||
} catch (Exception ignored) {
|
||||
} finally {
|
||||
driver.switchTo().defaultContent();
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void handleTestTask(int i) {
|
||||
try {
|
||||
driver.switchTo().frame(i).switchTo().frame(0);
|
||||
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//div[@class='singleQuesId']")));
|
||||
log.info("开始处理测验...");
|
||||
//所有题目元素
|
||||
List<WebElement> questions = driver.findElements(By.xpath(".//div[@class='singleQuesId']"));
|
||||
log.info("发现{}个题目...", questions.size());
|
||||
for (WebElement question : questions) {
|
||||
log.info("处理第{}个题目...", questions.indexOf(question));
|
||||
//获取题目截图,通过OCR获取题目信息
|
||||
WebElement questionTitleElement = question.findElement(By.xpath(".//div[@class='TiMu newTiMu']"));
|
||||
File screenshot = questionTitleElement.getScreenshotAs(OutputType.FILE);
|
||||
|
||||
String questionTitle = OcrUtil.getContentOfImage(screenshot);
|
||||
log.info("题目信息: {}", questionTitle);
|
||||
try {
|
||||
//尝试获取单选选项
|
||||
//li role='radio'
|
||||
question.findElement(By.xpath(".//li[@role='radio']"));
|
||||
List<WebElement> choiceElements = question.findElements(By.xpath(".//li[@role='radio']"));
|
||||
SingleChoiceQuestion singleChoiceQuestion = new SingleChoiceQuestion();
|
||||
singleChoiceQuestion.setTitle(questionTitle);
|
||||
List<String> choices = getChoices(choiceElements);
|
||||
singleChoiceQuestion.setChoices(choices);
|
||||
Answer primaryAnswer = getAnswer(singleChoiceQuestion);
|
||||
SingleChoiceAnswer answer = (SingleChoiceAnswer) primaryAnswer;
|
||||
String answerContent = answer.getAnswer();
|
||||
log.info("答案: {}", answerContent);
|
||||
String xpath = ".//span[@data='" + answerContent + "']";
|
||||
question.findElement(By.xpath(xpath)).findElement(By.xpath("..")).findElement(By.xpath("..")).click();
|
||||
continue;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
try {
|
||||
//尝试获取复选框
|
||||
//li role='checkbox'
|
||||
question.findElement(By.xpath(".//li[@role='checkbox']"));
|
||||
List<WebElement> choiceElements = question.findElements(By.xpath(".//li[@role='checkbox']"));
|
||||
MultiChoiceQuestion multiChoiceQuestion = new MultiChoiceQuestion();
|
||||
multiChoiceQuestion.setTitle(questionTitle);
|
||||
List<String> choices = getChoices(choiceElements);
|
||||
multiChoiceQuestion.setChoices(choices);
|
||||
|
||||
continue;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
try {
|
||||
//尝试跳转到简答题输入框
|
||||
driver.switchTo().frame("ueditor_0");
|
||||
driver.findElement(By.tagName("p")).sendKeys("answer");
|
||||
|
||||
driver.switchTo().parentFrame();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
log.info("答题任务完成!");
|
||||
} catch (Exception ignored) {
|
||||
} finally {
|
||||
driver.switchTo().defaultContent();
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> getChoices(List<WebElement> choiceElements) {
|
||||
List<String> choices = new ArrayList<>();
|
||||
for (WebElement choiceElement : choiceElements) {
|
||||
String label = choiceElement.findElement(By.tagName("label")).getText();
|
||||
String content = choiceElement.findElement(By.tagName("a")).getText();
|
||||
choices.add(String.format("%s. %s", label, content));
|
||||
}
|
||||
return choices;
|
||||
}
|
||||
|
||||
private static Answer getAnswer(Question question) throws InterruptedException {
|
||||
Answer answer = null;
|
||||
int tryTimes = 0;
|
||||
while (tryTimes < 3) {
|
||||
try {
|
||||
String questionStr = JSONUtil.toJsonPrettyStr(question);
|
||||
String responseStr = AiUtil.runChat(questionStr);
|
||||
// 使用正则表达式提取Markdown代码块中的JSON内容
|
||||
String jsonPattern = "```[\\s\\S]*?\\n([\\s\\S]*?)\\n```";
|
||||
Pattern pattern = Pattern.compile(jsonPattern);
|
||||
Matcher matcher = pattern.matcher(responseStr);
|
||||
String jsonStr = "";
|
||||
if (matcher.find()) {
|
||||
jsonStr = matcher.group(1).trim();
|
||||
}
|
||||
if (jsonStr.isEmpty()) {
|
||||
throw new Exception("AI 回答格式不匹配: " + jsonStr);
|
||||
}
|
||||
if (question instanceof SingleChoiceQuestion) {
|
||||
answer = JSONUtil.toBean(jsonStr, SingleChoiceAnswer.class);
|
||||
} else if (question instanceof MultiChoiceQuestion) {
|
||||
answer = JSONUtil.toBean(jsonStr, MultiChoiceAnswer.class);
|
||||
} else if (question instanceof TextQuestion) {
|
||||
answer = JSONUtil.toBean(jsonStr, TextAnswer.class);
|
||||
}
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getLocalizedMessage());
|
||||
log.error("3s后重试...");
|
||||
Thread.sleep(3000);
|
||||
tryTimes++;
|
||||
}
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
private static void handleVideoTask(int i) {
|
||||
try {
|
||||
driver.switchTo().frame(i);
|
||||
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//div[@class='prev_video_left']")));
|
||||
log.info("开始处理视频播放...");
|
||||
WebElement play = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//button[@class='vjs-big-play-button']")));
|
||||
Thread.sleep(2000);
|
||||
play.click();
|
||||
WebElement video = driver.findElement(By.id("video_html5_api"));
|
||||
//获取总时长
|
||||
String totalTime = driver.findElement(By.xpath("//span[@class='vjs-duration-display']")).getText();
|
||||
// 获取视频的位置信息
|
||||
Point location = video.getLocation();
|
||||
Dimension size = video.getSize();
|
||||
|
||||
int videoX = location.getX();
|
||||
int videoY = location.getY();
|
||||
int videoWidth = size.getWidth();
|
||||
int videoHeight = size.getHeight();
|
||||
|
||||
// 确定鼠标起始位置:进入视频区域
|
||||
int startX = videoX + videoWidth / 4;
|
||||
int startY = videoY + videoHeight / 4;
|
||||
actions.moveToElement(video, startX, startY).perform();
|
||||
Thread.sleep(500);
|
||||
|
||||
int step = 10; // 每次移动步长
|
||||
|
||||
while (true) {
|
||||
for (int j = 0; j < 20; j++) {
|
||||
int offsetX = (int) (Math.random() * step) - step / 2; // 随机左右移动
|
||||
int offsetY = (int) (Math.random() * step) - step / 2; // 随机上下移动
|
||||
actions.moveByOffset(offsetX, offsetY).perform();
|
||||
Thread.sleep(200);
|
||||
}
|
||||
String currentTime = driver.findElement(By.xpath(".//span[@class='vjs-current-time-display']")).getText();
|
||||
if (totalTime.equals("0:00") || totalTime.isEmpty()) {
|
||||
totalTime = driver.findElement(By.xpath("//span[@class='vjs-duration-display']")).getText();
|
||||
} else if (currentTime.equals(totalTime)) {
|
||||
log.info(totalTime);
|
||||
break;
|
||||
}
|
||||
log.info(currentTime + "/" + totalTime);
|
||||
//恢复暂停
|
||||
try {
|
||||
WebElement playingButton = driver.findElement(By.xpath(".//button[@class='vjs-play-control vjs-control vjs-button vjs-paused']"));
|
||||
playingButton.click();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
log.info("视频播放任务完成!");
|
||||
} catch (Exception ignored) {
|
||||
} finally {
|
||||
driver.switchTo().defaultContent();
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt("iframe"));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<WebElement> getTodoTasks() {
|
||||
driver.switchTo().defaultContent();
|
||||
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div[@class='posCatalog_select']")));
|
||||
List<WebElement> toBeFinished = new ArrayList<>();
|
||||
for (WebElement webElement : driver.findElements(By.xpath("//div[@class='posCatalog_select']"))) {
|
||||
try {
|
||||
webElement.findElement(By.xpath(".//span[@class='orangeNew']"));
|
||||
toBeFinished.add(webElement);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
log.info("获取到{}个任务", toBeFinished.size());
|
||||
return toBeFinished;
|
||||
}
|
||||
|
||||
private static void loginAndSwitch(String phone, String passwd, String targetClassName) {
|
||||
log.info("登录中...");
|
||||
driver.get("https://i.chaoxing.com/");
|
||||
WebElement phoneInput = driver.findElement(By.xpath("//input[@id='phone']"));
|
||||
phoneInput.sendKeys(phone);
|
||||
WebElement passwdInput = driver.findElement(By.xpath("//input[@id='pwd']"));
|
||||
passwdInput.sendKeys(passwd);
|
||||
WebElement submitInput = driver.findElement(By.xpath("//button[@id='loginBtn']"));
|
||||
submitInput.click();
|
||||
|
||||
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//div[@name='课程']"))).click();
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(0));
|
||||
String xpath = ".//span[@title='" + targetClassName + "']";
|
||||
String targetClassHref = wait.until(ExpectedConditions.elementToBeClickable(By.xpath(xpath))).findElement(By.xpath("..")).getDomAttribute("href");
|
||||
driver.get(targetClassHref);
|
||||
|
||||
driver.findElement(By.xpath("//a[@title='章节']")).click();
|
||||
wait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(0));
|
||||
wait.until(ExpectedConditions.elementToBeClickable(By.xpath(".//div[@role='link']"))).findElement(By.xpath("..")).click();
|
||||
log.info("登录成功!");
|
||||
}
|
||||
|
||||
private static void initialize() {
|
||||
WebDriverManager.firefoxdriver().setup();
|
||||
|
||||
driver = new FirefoxDriver();
|
||||
actions = new Actions(driver);
|
||||
wait = new WebDriverWait(driver, Duration.ofSeconds(5));
|
||||
log.info("webdriver创建完毕!");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package work.slhaf.task.cxstudy.pojo.answer;
|
||||
|
||||
public class Answer {
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package work.slhaf.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import work.slhaf.chat.ChatClient;
|
||||
import work.slhaf.chat.constant.Constant;
|
||||
import work.slhaf.chat.pojo.ChatResponse;
|
||||
import work.slhaf.chat.pojo.Message;
|
||||
import work.slhaf.config.Config;
|
||||
import work.slhaf.config.AiConfig;
|
||||
import work.slhaf.exception.AiRequestException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class AiUtil {
|
||||
private static ChatClient chatClient;
|
||||
private static final String PROMPT = """
|
||||
@@ -65,16 +68,16 @@ public class AiUtil {
|
||||
}
|
||||
```
|
||||
|
||||
注意:两种选择题的选项内容(不包含序号)可能存在乱码,当输入的Question JSON为选择题时,你需要同时根据choices和title中的内容来确定正确选项。
|
||||
注意:两种选择题的选项内容(不包含序号)可能存在乱码,当输入的Question JSON为选择题时,你需要同时根据choices和title中的内容来确定正确选项,同时确保答案中选项数量符合题意。
|
||||
请根据接收到的Question JSON,生成对应的Answer JSON,并将结果放入Markdown代码块中。确保答案内容合理且符合问题要求。
|
||||
""";
|
||||
|
||||
private AiUtil() {}
|
||||
|
||||
public static void load(Config config) {
|
||||
String apikey = config.getAiConfig().getApikey();
|
||||
String model = config.getAiConfig().getModel();
|
||||
String baseUrl = config.getAiConfig().getBaseUrl();
|
||||
public static void load(AiConfig config) {
|
||||
String apikey = config.getApikey();
|
||||
String model = config.getModel();
|
||||
String baseUrl = config.getBaseUrl();
|
||||
chatClient = new ChatClient(baseUrl, apikey, model);
|
||||
}
|
||||
|
||||
@@ -83,8 +86,13 @@ public class AiUtil {
|
||||
Message system = new Message(Constant.Character.SYSTEM,PROMPT);
|
||||
messages.add(system);
|
||||
messages.add(new Message(Constant.Character.USER,message));
|
||||
|
||||
ChatResponse chatResponse = chatClient.runChat(messages);
|
||||
ChatResponse chatResponse;
|
||||
try {
|
||||
chatResponse = chatClient.runChat(messages);
|
||||
}catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
throw new AiRequestException("ai答题运行出错,请检查配置或者关闭ai答题");
|
||||
}
|
||||
return chatResponse.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ import com.aliyun.ocr_api20210707.models.RecognizeAdvancedResponse;
|
||||
import com.aliyun.tea.TeaException;
|
||||
import com.aliyun.teautil.models.RuntimeOptions;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import work.slhaf.config.Config;
|
||||
import work.slhaf.config.OcrConfig;
|
||||
import work.slhaf.exception.OcrRequestException;
|
||||
import work.slhaf.pojo.OCRDataInfo;
|
||||
|
||||
import java.io.File;
|
||||
@@ -20,10 +21,10 @@ public class OcrUtil {
|
||||
|
||||
private OcrUtil() {}
|
||||
|
||||
public static void load(Config basicConfig) {
|
||||
public static void load(OcrConfig basicConfig) {
|
||||
//读取密钥
|
||||
String accessKeyId = basicConfig.getOcrConfig().getAccessKeyId();
|
||||
String accessKeySecret = basicConfig.getOcrConfig().getAccessKeySecret();
|
||||
String accessKeyId = basicConfig.getAccessKeyId();
|
||||
String accessKeySecret = basicConfig.getAccessKeySecret();
|
||||
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
|
||||
.setAccessKeyId(accessKeyId)
|
||||
.setAccessKeySecret(accessKeySecret);
|
||||
@@ -67,18 +68,10 @@ public class OcrUtil {
|
||||
log.error(error.getMessage());
|
||||
// 诊断地址
|
||||
log.error(error.getData().get("Recommend").toString());
|
||||
com.aliyun.teautil.Common.assertAsString(error.message);
|
||||
return "ERROR";
|
||||
} catch (Exception _error) {
|
||||
TeaException error = new TeaException(_error.getMessage(), _error);
|
||||
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
|
||||
// 错误 message
|
||||
log.error(error.getMessage());
|
||||
// 诊断地址
|
||||
log.error(error.getData().get("Recommend").toString());
|
||||
com.aliyun.teautil.Common.assertAsString(error.message);
|
||||
return "ERROR";
|
||||
}
|
||||
throw new OcrRequestException("OCR请求出错,检查配置或者关闭ai答题");
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
throw new OcrRequestException("OCR请求出错,检查配置或者关闭ai答题"); }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user