From badbd0f1c4a7ebf231e118b9a330f6a6d5bb134c Mon Sep 17 00:00:00 2001 From: wufei-png Date: Sun, 12 Oct 2025 02:04:55 +0800 Subject: [PATCH 1/7] feat: remove checkV2 and use checkV3 (but now checkV3 also should use right default value) --- .../com/github/pengpan/client/MainClient.java | 22 +++- .../common/constant/SystemConstant.java | 4 +- .../github/pengpan/entity/CheckUserV3.java | 100 +++++++++++++++ .../github/pengpan/service/LoginService.java | 6 +- .../service/impl/LoginServiceImpl.java | 115 +++++++++++++----- 5 files changed, 210 insertions(+), 37 deletions(-) create mode 100644 src/main/java/com/github/pengpan/entity/CheckUserV3.java diff --git a/src/main/java/com/github/pengpan/client/MainClient.java b/src/main/java/com/github/pengpan/client/MainClient.java index 1d0eeac..326e3f0 100644 --- a/src/main/java/com/github/pengpan/client/MainClient.java +++ b/src/main/java/com/github/pengpan/client/MainClient.java @@ -1,16 +1,26 @@ package com.github.pengpan.client; +import java.util.List; +import java.util.Map; + import com.github.pengpan.common.proxy.Proxy; import com.github.pengpan.common.retry.Retry; import com.github.pengpan.entity.BrushSch; import com.github.pengpan.entity.CheckUser; +import com.github.pengpan.entity.CheckUserV3; import com.github.pengpan.entity.DoctorSch; + import okhttp3.ResponseBody; import retrofit2.Response; -import retrofit2.http.*; - -import java.util.List; -import java.util.Map; +import retrofit2.http.Field; +import retrofit2.http.FieldMap; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.GET; +import retrofit2.http.Headers; +import retrofit2.http.POST; +import retrofit2.http.Path; +import retrofit2.http.Query; +import retrofit2.http.Url; /** * @author pengpan @@ -24,6 +34,10 @@ public interface MainClient { @POST CheckUser checkUser(@Url String url, @FieldMap Map fields); + @FormUrlEncoded + @POST + CheckUserV3 checkUserV3(@Url String url, @FieldMap Map fields); + @FormUrlEncoded @POST Response doLogin(@Url String url, @FieldMap Map fields); diff --git a/src/main/java/com/github/pengpan/common/constant/SystemConstant.java b/src/main/java/com/github/pengpan/common/constant/SystemConstant.java index 68283ca..dc59898 100644 --- a/src/main/java/com/github/pengpan/common/constant/SystemConstant.java +++ b/src/main/java/com/github/pengpan/common/constant/SystemConstant.java @@ -9,7 +9,7 @@ public class SystemConstant { public final static String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDWuY4Gff8FO3BAKetyvNgGrdZM9CMNoe45SzHMXxAPWw6E2idaEjqe5uJFjVx55JW+5LUSGO1H5MdTcgGEfh62ink/cNjRGJpR25iVDImJlLi2izNs9zrQukncnpj6NGjZu/2z7XXfJb4XBwlrmR823hpCumSD1WiMl1FMfbVorQIDAQAB"; - public final static String DEFECT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36"; + public final static String DEFECT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"; public final static String DOMAIN = "https://www.91160.com"; @@ -22,6 +22,8 @@ public class SystemConstant { public final static String LOGIN_URL = "https://user.91160.com/login.html"; public final static String CHECK_USER_URL = "https://user.91160.com/checkUser.html"; + + public final static String CHECK_USER_V3_URL = "https://www.91160.com/user/check.html"; public final static String CAPTCHA_URL = "https://user.91160.com/Captcha.png"; diff --git a/src/main/java/com/github/pengpan/entity/CheckUserV3.java b/src/main/java/com/github/pengpan/entity/CheckUserV3.java new file mode 100644 index 0000000..6ad0ff6 --- /dev/null +++ b/src/main/java/com/github/pengpan/entity/CheckUserV3.java @@ -0,0 +1,100 @@ +package com.github.pengpan.entity; + +import java.util.HashMap; +import java.util.Map; + +import lombok.Data; + +/** + * @author wufei + */ +@Data +public class CheckUserV3 { + + private String access_token; + + private String avatar; + + private String cat_id; + + private String city_id; + + private String email; + + private String fid; + + private String health_card; + + private String id; + + private String name; + + private String nickname; + + private String phone; + + private String sex; + + private String userId; + + private String user_age; + + private String user_id; + + private String user_ip; + + private String user_sex; + + /** + * 创建带有默认值的 CheckUserV3 实例 + * @return CheckUserV3 实例 + */ + public static CheckUserV3 createDefault() { + //TODO 应该从用户配置中获取 + CheckUserV3 checkUserV3 = new CheckUserV3(); + checkUserV3.setAccess_token("111"); + checkUserV3.setAvatar("https://wximg.91160.com/wechat/img/avatar/5.png"); + checkUserV3.setCat_id(""); + checkUserV3.setCity_id(""); + checkUserV3.setEmail(""); + checkUserV3.setFid("296013981"); + checkUserV3.setHealth_card(""); + checkUserV3.setId(""); + checkUserV3.setName("1"); + checkUserV3.setNickname("1"); + checkUserV3.setPhone("123456789"); + checkUserV3.setSex("0"); + checkUserV3.setUserId(""); + checkUserV3.setUser_age(""); + checkUserV3.setUser_id(""); + checkUserV3.setUser_ip("163.125.146.76"); + checkUserV3.setUser_sex(""); + return checkUserV3; + } + + /** + * 将 CheckUserV3 对象转换为 Map + * @return Map + */ + public Map toMap() { + Map map = new HashMap<>(); + map.put("access_token", this.access_token); + map.put("avatar", this.avatar); + map.put("cat_id", this.cat_id); + map.put("city_id", this.city_id); + map.put("email", this.email); + map.put("fid", this.fid); + map.put("health_card", this.health_card); + map.put("id", this.id); + map.put("name", this.name); + map.put("nickname", this.nickname); + map.put("phone", this.phone); + map.put("sex", this.sex); + map.put("userId", this.userId); + map.put("user_age", this.user_age); + map.put("user_id", this.user_id); + map.put("user_ip", this.user_ip); + map.put("user_sex", this.user_sex); + return map; + } +} diff --git a/src/main/java/com/github/pengpan/service/LoginService.java b/src/main/java/com/github/pengpan/service/LoginService.java index 879629d..7b4204c 100644 --- a/src/main/java/com/github/pengpan/service/LoginService.java +++ b/src/main/java/com/github/pengpan/service/LoginService.java @@ -1,9 +1,9 @@ package com.github.pengpan.service; -import com.github.pengpan.enums.LoginResultEnum; - import java.awt.image.BufferedImage; +import com.github.pengpan.enums.LoginResultEnum; + /** * @author pengpan */ @@ -33,4 +33,6 @@ public interface LoginService { boolean doLogon(String username, String password); boolean doLoginRetry(String username, String password, int retries); + + boolean checkUserV3(String username, String password, String token, String code); } diff --git a/src/main/java/com/github/pengpan/service/impl/LoginServiceImpl.java b/src/main/java/com/github/pengpan/service/impl/LoginServiceImpl.java index d40e3f9..87ae4f2 100644 --- a/src/main/java/com/github/pengpan/service/impl/LoginServiceImpl.java +++ b/src/main/java/com/github/pengpan/service/impl/LoginServiceImpl.java @@ -1,21 +1,25 @@ package com.github.pengpan.service.impl; -import cn.hutool.core.codec.Base64; -import cn.hutool.core.img.ImgUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.thread.ThreadUtil; -import cn.hutool.core.util.RandomUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.SecureUtil; -import cn.hutool.crypto.asymmetric.KeyType; -import cn.hutool.crypto.asymmetric.RSA; -import cn.hutool.http.HttpUtil; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Resource; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.springframework.stereotype.Service; + import com.ejlchina.json.JSONKit; import com.github.pengpan.client.MainClient; import com.github.pengpan.common.constant.SystemConstant; import com.github.pengpan.common.store.AccountStore; import com.github.pengpan.common.store.ConfigStore; import com.github.pengpan.entity.CheckUser; +import com.github.pengpan.entity.CheckUserV3; import com.github.pengpan.entity.fateadm.CapRegResult; import com.github.pengpan.enums.LoginResultEnum; import com.github.pengpan.enums.OcrPlatformEnum; @@ -23,21 +27,21 @@ import com.github.pengpan.service.DdddOcrService; import com.github.pengpan.service.LoginService; import com.github.pengpan.util.Assert; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.img.ImgUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.crypto.asymmetric.RSA; +import cn.hutool.http.HttpUtil; import lombok.extern.slf4j.Slf4j; import okhttp3.ResponseBody; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.springframework.stereotype.Service; import retrofit2.Response; -import javax.annotation.Resource; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - /** * @author pengpan */ @@ -169,6 +173,13 @@ public boolean checkUserV2(String username, String password, String token, Strin @Override public LoginResultEnum loginV2(String username, String password, String token, String code) { + // Add human-like delay before login + try { + Thread.sleep(RandomUtil.randomInt(1000, 3000)); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + String encryptedUsername = Base64.encode(rsa.encrypt(username, KeyType.PublicKey)); String encryptedPassword = Base64.encode(rsa.encrypt(password, KeyType.PublicKey)); @@ -224,10 +235,8 @@ public boolean doLoginV2(String username, String password) { .map(CapRegResult::getResult) .orElseGet(String::new); - boolean checkUser = checkUserV2(username, password, token, code); - if (!checkUser) { - return false; - } + // Skip checkUserV2 to bypass anti-bot detection + log.info("Skipping checkUserV2 because it is duplicate"); LoginResultEnum loginResult = loginV2(username, password, token, code); if (loginResult == LoginResultEnum.SUCCESS) { @@ -236,6 +245,10 @@ public boolean doLoginV2(String username, String password) { log.info("code: " + code); // collect captchaCollect(captchaBase64, code); + boolean checkUserv3 = checkUserV3(username, password, token, code); + if (!checkUserv3) { + return false; + } return true; } if (loginResult == LoginResultEnum.CAPTCHA_INCORRECT) { @@ -255,11 +268,9 @@ public boolean doLoginV3(String username, String password) { BufferedImage captchaImage = getCaptchaImage(); String code = ddddOcrService.ocr(captchaImage); - boolean checkUser = checkUserV2(username, password, token, code); - if (!checkUser) { - return false; - } - + // Skip checkUserV2 to bypass anti-bot detection + log.info("Skipping checkUserV2 because it is deprecated"); + LoginResultEnum loginResult = loginV2(username, password, token, code); if (loginResult == LoginResultEnum.SUCCESS) { String captchaBase64 = ImgUtil.toBase64DataUri(captchaImage, "png"); @@ -267,6 +278,11 @@ public boolean doLoginV3(String username, String password) { log.info("code: " + code); // collect captchaCollect(captchaBase64, code); + + boolean checkUserv3 = checkUserV3(username, password, token, code); + if (!checkUserv3) { + return false; + } return true; } return false; @@ -313,4 +329,43 @@ public boolean doLoginRetry(String username, String password, int retries) { return doLoginRetry(username, password, retries - 1); } } + + @Override + public boolean checkUserV3(String username, String password, String token, String code) { + // Add human-like delay before checkUserV3 + try { + Thread.sleep(RandomUtil.randomInt(2000, 5000)); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + log.info("checkUserV3"); + + String encryptedUsername = Base64.encode(rsa.encrypt(username, KeyType.PublicKey)); + String encryptedPassword = Base64.encode(rsa.encrypt(password, KeyType.PublicKey)); + + // 创建默认的 CheckUserV3 实例并转换为 Map + CheckUserV3 defaultCheckUserV3 = CheckUserV3.createDefault(); + Map fields = defaultCheckUserV3.toMap(); + + // 添加登录相关的字段 + fields.put("username", encryptedUsername); + fields.put("password", encryptedPassword); + fields.put("type", "m"); + fields.put("token", token); + fields.put("checkcode", code); + + CheckUserV3 checkUserV3 = mainClient.checkUserV3(SystemConstant.CHECK_USER_V3_URL, fields); + Assert.notNull(checkUserV3, "用户检测V3失败"); + + log.info("CheckUserV3 response: {}", checkUserV3); + + // 由于CheckUserV3没有code字段,我们检查是否有access_token返回来判断成功 + if (StrUtil.isNotBlank(checkUserV3.getAccess_token())) { + log.info("用户检测V3通过"); + return true; + } else { + log.warn("用户检测V3不通过"); + return false; + } + } } From 8b20cf547d5c105b18d04b5e365903609e9ad6eb Mon Sep 17 00:00:00 2001 From: wufei-png Date: Sun, 12 Oct 2025 02:06:50 +0800 Subject: [PATCH 2/7] feat: add more fields for submit --- .../com/github/pengpan/client/MainClient.java | 24 +++- .../com/github/pengpan/entity/Register.java | 22 ++- .../pengpan/service/impl/CoreServiceImpl.java | 135 +++++++++++++----- 3 files changed, 134 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/github/pengpan/client/MainClient.java b/src/main/java/com/github/pengpan/client/MainClient.java index 326e3f0..035869d 100644 --- a/src/main/java/com/github/pengpan/client/MainClient.java +++ b/src/main/java/com/github/pengpan/client/MainClient.java @@ -87,19 +87,29 @@ String orderPage(@Path("unitId") String unitId, @FormUrlEncoded @POST("/guahao/ysubmit.html") Response doSubmit(@Field("sch_data") String schData, - @Field("unit_id") String unitId, - @Field("dep_id") String depId, - @Field("doctor_id") String doctorId, - @Field("schedule_id") String schId, @Field("mid") String memberId, + @Field("addressId") String addressId, + @Field("address") String address, + @Field("disease_input") String diseaseInput, + @Field("order_no") String orderNo, + @Field("disease_content") String diseaseContent, @Field("accept") String accept, + @Field("unit_id") String unitId, + @Field("schedule_id") String schId, + @Field("dep_id") String depId, + @Field("his_dep_id") String hisDepId, + @Field("sch_date") String schDate, @Field("time_type") String time_type, + @Field("doctor_id") String doctorId, + @Field("his_doc_id") String hisDocId, @Field("detlid") String detlid, @Field("detlid_realtime") String detlid_realtime, @Field("level_code") String level_code, - @Field("addressId") String addressId, - @Field("address") String address, - @Field("hisMemId") String hisMemId); + @Field("is_hot") String isHot, + @Field("pay_online") String payOnline, + @Field("detl_name") String detlName, + @Field("to_date") String toDate, + @Field("his_mem_id") String hisMemId); @GET("/favicon.ico") Response serverTime(); diff --git a/src/main/java/com/github/pengpan/entity/Register.java b/src/main/java/com/github/pengpan/entity/Register.java index 8d94438..7cf0fd0 100644 --- a/src/main/java/com/github/pengpan/entity/Register.java +++ b/src/main/java/com/github/pengpan/entity/Register.java @@ -11,19 +11,27 @@ public class Register { private String schData; - private String unitId; - private String depId; - private String doctorId; - private String schId; private String memberId; + private String addressId; + private String address; + private String diseaseInput; + private String orderNo; + private String diseaseContent; private String accept; + private String unitId; + private String schId; + private String depId; + private String hisDepId; + private String schDate; private String timeType; - private String detlName; + private String doctorId; + private String hisDocId; private String detlid; private String detlidRealtime; private String levelCode; - private String addressId; - private String address; + private String isHot; + private String payOnline; + private String detlName; private String toDate; private String hisMemId; } diff --git a/src/main/java/com/github/pengpan/service/impl/CoreServiceImpl.java b/src/main/java/com/github/pengpan/service/impl/CoreServiceImpl.java index c942df0..bfa4324 100644 --- a/src/main/java/com/github/pengpan/service/impl/CoreServiceImpl.java +++ b/src/main/java/com/github/pengpan/service/impl/CoreServiceImpl.java @@ -1,40 +1,57 @@ package com.github.pengpan.service.impl; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.date.DatePattern; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.text.UnicodeUtil; -import cn.hutool.core.thread.ThreadUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.http.HttpUtil; -import cn.hutool.setting.dialect.PropsUtil; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import javax.annotation.Resource; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.springframework.stereotype.Service; + import com.ejlchina.data.TypeRef; import com.ejlchina.json.JSONKit; import com.github.pengpan.client.MainClient; import com.github.pengpan.common.constant.SystemConstant; import com.github.pengpan.common.cookie.CookieStore; -import com.github.pengpan.entity.*; +import com.github.pengpan.entity.Appointment; +import com.github.pengpan.entity.BrushSch; +import com.github.pengpan.entity.BrushSchData; +import com.github.pengpan.entity.Config; +import com.github.pengpan.entity.Register; +import com.github.pengpan.entity.ScheduleInfo; import com.github.pengpan.enums.BrushChannelEnum; import com.github.pengpan.enums.DataTypeEnum; import com.github.pengpan.service.BrushService; import com.github.pengpan.service.CoreService; import com.github.pengpan.util.Assert; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.text.UnicodeUtil; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.setting.dialect.PropsUtil; import lombok.extern.slf4j.Slf4j; import okhttp3.Cookie; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; -import org.springframework.stereotype.Service; import retrofit2.Response; -import javax.annotation.Resource; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - /** * @author pengpan */ @@ -200,8 +217,8 @@ public void brushTicketTask(Config config) { List schInfoList = brushService.getTicket(config); if (CollUtil.isEmpty(schInfoList)) { - // 休眠 - ThreadUtil.sleep(config.getSleepTime(), TimeUnit.MILLISECONDS); + // 休眠 - 使用配置的随机延迟时间 + ThreadUtil.sleep(config.getRandomSleepTime(), TimeUnit.MILLISECONDS); continue; } @@ -222,8 +239,8 @@ public void brushTicketTask(Config config) { } if (CollUtil.isEmpty(formList)) { - // 休眠 - ThreadUtil.sleep(config.getSleepTime(), TimeUnit.MILLISECONDS); + // 休眠 - 使用配置的随机延迟时间 + ThreadUtil.sleep(config.getRandomSleepTime(), TimeUnit.MILLISECONDS); continue; } @@ -296,33 +313,76 @@ private boolean doRegister(List formList) { Response submitResp = mainClient.doSubmit( form.getSchData(), - form.getUnitId(), - form.getDepId(), - form.getDoctorId(), - form.getSchId(), form.getMemberId(), + form.getAddressId(), + form.getAddress(), + form.getDiseaseInput(), + form.getOrderNo(), + form.getDiseaseContent(), form.getAccept(), + form.getUnitId(), + form.getSchId(), + form.getDepId(), + form.getHisDepId(), + form.getSchDate(), form.getTimeType(), + form.getDoctorId(), + form.getHisDocId(), form.getDetlid(), form.getDetlidRealtime(), form.getLevelCode(), - form.getAddressId(), - form.getAddress(), + form.getIsHot(), + form.getPayOnline(), + form.getDetlName(), + form.getToDate(), form.getHisMemId() ); - + // 详细打印响应信息 + log.info("=== 提交响应详情 ==="); + log.info("响应状态码: {}", submitResp.code()); + log.info("响应消息: {}", submitResp.message()); + log.info("是否成功: {}", submitResp.isSuccessful()); + log.info("是否重定向: {}", submitResp.raw().isRedirect()); + + // 打印响应头信息 + log.info("响应头信息:"); + submitResp.headers().toMultimap().forEach((key, values) -> + log.info(" {}: {}", key, String.join(", ", values))); + + // 打印响应体(如果有的话) + if (submitResp.body() != null) { + log.info("响应体: {}", submitResp.body()); + } + + // 打印原始响应信息 + log.info("原始响应: {}", submitResp.raw()); + + // 检查响应状态 + // if (!submitResp.isSuccessful()) { + // log.warn("请求失败 - 状态码: {}, 消息: {}", submitResp.code(), submitResp.message()); + // failCount.put(form.getSchId(), failCount.getOrDefault(form.getSchId(), 0) + 1); + // continue; + // } + if (!submitResp.raw().isRedirect()) { + log.warn("请求成功但未重定向 - 状态码: {}", submitResp.code()); + failCount.put(form.getSchId(), failCount.getOrDefault(form.getSchId(), 0) + 1); continue; } String redirectUrl = submitResp.headers().get("Location"); + log.info("Submitting with params: {}", JSONKit.toJson(form)); + log.debug("schData (raw): {}", form.getSchData()); + log.debug("schData length: {}", form.getSchData().length()); + log.info("redirectUrl: {}", redirectUrl); String html = mainClient.htmlPage(redirectUrl); + // log.info("Response HTML: {}", html); // 判断结果 if (StrUtil.contains(html, "预约成功")) { log.info("预约成功"); return true; } - log.info("预约失败:{}次 ({} {})", count + 1, form.getToDate(), form.getDetlName()); + log.info("预约失败:{}次 ({} {})", count + 1, form.getSchDate(), form.getSchId()); failCount.put(form.getSchId(), ++count); } @@ -385,10 +445,13 @@ private List buildForm(ScheduleInfo schInfo, Config config) { .depId(config.getDeptId()) .doctorId(schInfo.getDoctor_id()) .schId(schInfo.getSchedule_id()) + .hisDepId("") + .schDate(schInfo.getTo_date()) .memberId(config.getMemberId()) .accept("1") .timeType(schInfo.getTime_type()) .detlName(x.getName()) + .hisDocId("") .detlid(x.getValue()) .detlidRealtime(detlid_realtime) .levelCode(level_code) @@ -396,6 +459,12 @@ private List buildForm(ScheduleInfo schInfo, Config config) { .address("Civic Center") .toDate(schInfo.getTo_date()) .hisMemId(StrUtil.blankToDefault(config.getMedicalCard(), null)) + .diseaseInput("11111111111111") + .orderNo("") + .diseaseContent("11111111111111") + .accept("1") + .payOnline("0") + .isHot("") .build()) .collect(Collectors.toList()); } From afa7a3a89b7a05ed862f53173419309b888530d4 Mon Sep 17 00:00:00 2001 From: wufei-png Date: Sun, 12 Oct 2025 02:12:10 +0800 Subject: [PATCH 3/7] feat: add random sleep time support --- .../java/com/github/pengpan/cmd/Register.java | 2 +- .../com/github/pengpan/entity/Config.java | 48 +++++++++++++++++-- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/github/pengpan/cmd/Register.java b/src/main/java/com/github/pengpan/cmd/Register.java index 99a68ab..a5da96d 100644 --- a/src/main/java/com/github/pengpan/cmd/Register.java +++ b/src/main/java/com/github/pengpan/cmd/Register.java @@ -184,7 +184,7 @@ private void checkBasicConfig(Config config) { Assert.notEmpty(config.getDoctorId(), "[doctorId]不能为空,请检查配置文件"); Assert.notEmpty(config.getWeeks(), "[weeks]不能为空,请检查配置文件"); Assert.notEmpty(config.getDays(), "[days]不能为空,请检查配置文件"); - Assert.isTrue(config.getSleepTime() >= 0, "[sleepTime]格式不正确,请检查配置文件"); + Assert.isTrue(config.getRandomSleepTime() >= 0, "[sleepTime]格式不正确,请检查配置文件, 例如 \"sleepTime=3000-8000\" "); if (StrUtil.isNotBlank(config.getBrushStartDate())) { Date brushStartDate = CommonUtil.parseDate(config.getBrushStartDate(), DatePattern.NORM_DATE_PATTERN); Assert.notNull(brushStartDate, "[brushStartDate]格式不正确,请检查配置文件"); diff --git a/src/main/java/com/github/pengpan/entity/Config.java b/src/main/java/com/github/pengpan/entity/Config.java index 6bce5c9..905602e 100644 --- a/src/main/java/com/github/pengpan/entity/Config.java +++ b/src/main/java/com/github/pengpan/entity/Config.java @@ -1,16 +1,21 @@ package com.github.pengpan.entity; +import java.util.List; + import com.github.pengpan.enums.BrushChannelEnum; import com.github.pengpan.enums.OcrPlatformEnum; import com.github.pengpan.enums.ProxyModeEnum; -import lombok.Data; -import java.util.List; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; /** * @author pengpan */ @Data +@Slf4j public class Config { /** @@ -69,9 +74,9 @@ public class Config { private String memberId; /** - * 刷号休眠时间[单位:毫秒] + * 刷号休眠时间[单位:毫秒],支持范围格式如: 3000-8000 */ - private int sleepTime; + private String sleepTime; /** * 刷号起始日期(表示刷该日期后一周的号,为空取当前日期)[格式: 2022-06-01] @@ -132,4 +137,39 @@ public class Config { * 91160-ocr-server的服务地址 */ private String baseUrl; + + /** + * 获取随机休眠时间(毫秒) + * 支持格式: "3000" 或 "3000-8000" + * @return 随机休眠时间 + */ + public int getRandomSleepTime() { + if (StrUtil.isBlank(sleepTime)) { + return 3000; // 默认值 + } + + if (sleepTime.contains("-")) { + // 范围格式: 3000-8000 + String[] parts = sleepTime.split("-"); + if (parts.length == 2) { + try { + int min = Integer.parseInt(parts[0].trim()); + int max = Integer.parseInt(parts[1].trim()); + log.info("随机休眠时间范围: {} - {}", min, max); + return RandomUtil.randomInt(min, max + 1); + } catch (NumberFormatException e) { + return 3000; // 解析失败,返回默认值 + } + } + } else { + // 固定值格式: 3000 + try { + return Integer.parseInt(sleepTime.trim()); + } catch (NumberFormatException e) { + return 3000; // 解析失败,返回默认值 + } + } + + return 3000; // 默认值 + } } From ffd7f6baa8626fae14f45eddab5e3c7e81e6a8e6 Mon Sep 17 00:00:00 2001 From: wufei-png Date: Sun, 12 Oct 2025 02:12:53 +0800 Subject: [PATCH 4/7] chore: update sleep time --- .../github/pengpan/service/impl/LoginServiceImpl.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/main/java/com/github/pengpan/service/impl/LoginServiceImpl.java b/src/main/java/com/github/pengpan/service/impl/LoginServiceImpl.java index 87ae4f2..8b77820 100644 --- a/src/main/java/com/github/pengpan/service/impl/LoginServiceImpl.java +++ b/src/main/java/com/github/pengpan/service/impl/LoginServiceImpl.java @@ -173,13 +173,6 @@ public boolean checkUserV2(String username, String password, String token, Strin @Override public LoginResultEnum loginV2(String username, String password, String token, String code) { - // Add human-like delay before login - try { - Thread.sleep(RandomUtil.randomInt(1000, 3000)); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - String encryptedUsername = Base64.encode(rsa.encrypt(username, KeyType.PublicKey)); String encryptedPassword = Base64.encode(rsa.encrypt(password, KeyType.PublicKey)); @@ -334,7 +327,7 @@ public boolean doLoginRetry(String username, String password, int retries) { public boolean checkUserV3(String username, String password, String token, String code) { // Add human-like delay before checkUserV3 try { - Thread.sleep(RandomUtil.randomInt(2000, 5000)); + Thread.sleep(RandomUtil.randomInt(1000, 2000)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } From 696adad35600c162cc6e902b810dbb0920d5bb66 Mon Sep 17 00:00:00 2001 From: wufei-png Date: Sun, 12 Oct 2025 02:13:53 +0800 Subject: [PATCH 5/7] feat: add cookies and header for submit req --- .../common/cookie/DefaultCookieConfig.java | 155 +++++++++++++ .../interceptor/MainClientInterceptor.java | 204 +++++++++++++++++- 2 files changed, 348 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/github/pengpan/common/cookie/DefaultCookieConfig.java diff --git a/src/main/java/com/github/pengpan/common/cookie/DefaultCookieConfig.java b/src/main/java/com/github/pengpan/common/cookie/DefaultCookieConfig.java new file mode 100644 index 0000000..27eb826 --- /dev/null +++ b/src/main/java/com/github/pengpan/common/cookie/DefaultCookieConfig.java @@ -0,0 +1,155 @@ +package com.github.pengpan.common.cookie; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import okhttp3.Cookie; + +/** + * 默认Cookie配置 + * 包含固定的Cookie值和动态生成的值 + * + * @author wufei + */ +@Slf4j +public class DefaultCookieConfig { + //TODO 应该从用户配置中获取 + // 固定的Cookie值(从header.yaml中提取) + private static final String FIXED_COOKIES = ""; + + /** + * 生成动态Cookie值 + */ + public static class DynamicCookies { + + /** + * 生成DKLFFDKD Cookie(加密令牌) + */ + public static String generateDKLFFDKD() { + String key = RandomUtil.randomString(32); + String val = RandomUtil.randomString(32); + long tm = System.currentTimeMillis() / 1000; + + String json = String.format("{\"key\":\"%s\",\"val\":\"%s\",\"tm\":%d}", key, val, tm); + return java.net.URLEncoder.encode(json, java.nio.charset.StandardCharsets.UTF_8); + } + + /** + * 生成FISKCDDCC Cookie(另一个加密令牌) + */ + public static String generateFISKCDDCC() { + return RandomUtil.randomString(32); + } + + /** + * 生成__guid Cookie(全局唯一标识符) + */ + public static String generateGuid() { + return RandomUtil.randomString(24) + "." + RandomUtil.randomInt(10000000, 99999999); + } + + /** + * 生成__jsluid_s Cookie(JavaScript库唯一ID) + */ + public static String generateJsluidS() { + return RandomUtil.randomString(32); + } + + /** + * 生成access_hash Cookie(访问哈希值) + */ + public static String generateAccessHash() { + String hash = RandomUtil.randomString(32); + String suffix = RandomUtil.randomString(12) + System.currentTimeMillis(); + return hash + suffix; + } + + /** + * 生成PHPSESSID Cookie(PHP会话ID) + */ + public static String generatePhpSessionId() { + return RandomUtil.randomString(26); + } + } + + /** + * 获取默认Cookie字符串 + * 包含固定Cookie和动态生成的Cookie + */ + public static String getDefaultCookieString() { + StringBuilder cookieBuilder = new StringBuilder(); + + // 添加动态生成的Cookie + cookieBuilder.append("DKLFFDKD=").append(DynamicCookies.generateDKLFFDKD()).append("; "); + cookieBuilder.append("PHPSESSID=").append(DynamicCookies.generatePhpSessionId()).append("; "); + cookieBuilder.append("__jsluid_s=").append(DynamicCookies.generateJsluidS()).append("; "); + cookieBuilder.append("autoLoginInfo=0e9fe9374589fdb45bf40ad5f50e5e8f; "); + cookieBuilder.append("__guid=").append(DynamicCookies.generateGuid()).append("; "); + cookieBuilder.append("access_hash=").append(DynamicCookies.generateAccessHash()).append("; "); + cookieBuilder.append("ip_city=sz; "); + cookieBuilder.append("FISKCDDCC=").append(DynamicCookies.generateFISKCDDCC()).append("; "); + + // 添加固定的Cookie + cookieBuilder.append(FIXED_COOKIES); + + return cookieBuilder.toString(); + } + + /** + * 将Cookie字符串转换为Cookie对象列表 + */ + public static List parseCookieString(String cookieString, String domain) { + List cookies = new ArrayList<>(); + + if (StrUtil.isBlank(cookieString)) { + return cookies; + } + + String[] cookiePairs = cookieString.split(";"); + for (String pair : cookiePairs) { + pair = pair.trim(); + if (StrUtil.isBlank(pair)) { + continue; + } + + String[] keyValue = pair.split("=", 2); + if (keyValue.length == 2) { + String name = keyValue[0].trim(); + String value = keyValue[1].trim(); + + // 创建Cookie对象,设置较长的过期时间 + Cookie cookie = new Cookie.Builder() + .name(name) + .value(value) + .domain(domain) + .path("/") + .expiresAt(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(30)) + .build(); + + cookies.add(cookie); + } + } + + return cookies; + } + + /** + * 获取www.91160.com的默认Cookie + */ + public static List getDefaultWwwCookies() { + String cookieString = getDefaultCookieString(); + return parseCookieString(cookieString, "www.91160.com"); + } + + /** + * 获取user.91160.com的默认Cookie + */ + public static List getDefaultUserCookies() { + String cookieString = getDefaultCookieString(); + return parseCookieString(cookieString, "user.91160.com"); + } +} diff --git a/src/main/java/com/github/pengpan/interceptor/MainClientInterceptor.java b/src/main/java/com/github/pengpan/interceptor/MainClientInterceptor.java index f1be5ca..8f0f503 100644 --- a/src/main/java/com/github/pengpan/interceptor/MainClientInterceptor.java +++ b/src/main/java/com/github/pengpan/interceptor/MainClientInterceptor.java @@ -1,28 +1,210 @@ package com.github.pengpan.interceptor; +import java.io.IOException; +import java.util.List; + import com.github.pengpan.common.constant.SystemConstant; +import com.github.pengpan.common.cookie.CookieStore; +import com.github.pengpan.common.cookie.DefaultCookieConfig; + +import lombok.extern.slf4j.Slf4j; +import okhttp3.Cookie; +import okhttp3.HttpUrl; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; - -import java.io.IOException; - /** * @author pengpan */ +@Slf4j public class MainClientInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); - Request newReq = request.newBuilder() - .addHeader("User-Agent", SystemConstant.DEFECT_USER_AGENT) - .addHeader("Referer", SystemConstant.DOMAIN) - .addHeader("Origin", SystemConstant.DOMAIN) - .addHeader("Connection", "keep-alive") - .addHeader("Pragma", "no-cache") - .addHeader("Cache-Control", "no-cache") - .build(); + + + Request.Builder requestBuilder = request.newBuilder(); + + // Add cookies for 91160.com domains + HttpUrl url = request.url(); + log.info("Request URL: {}", url.toString()); + boolean isSubmitRequest = url.toString().contains("ysubmit"); + log.debug("Is submit request: {}", isSubmitRequest); + + if (url.host().contains("91160.com")) { + StringBuilder cookieHeader = new StringBuilder(); + + // Get cookies from both user.91160.com and www.91160.com + List userCookies = CookieStore.get("user.91160.com"); + List wwwCookies = CookieStore.get("www.91160.com"); + + // Debug: Print available cookies + log.info("=== Cookie Info ==="); + log.info("Request URL: {}", url.toString()); + log.info("User cookies count: {}", userCookies != null ? userCookies.size() : 0); + log.info("WWW cookies count: {}", wwwCookies != null ? wwwCookies.size() : 0); + + if (userCookies != null && !userCookies.isEmpty()) { + log.info("User cookies:"); + for (Cookie cookie : userCookies) { + log.info(" {}={} (expires: {})", cookie.name(), cookie.value(), cookie.expiresAt()); + } + } + + if (wwwCookies != null && !wwwCookies.isEmpty()) { + log.info("WWW cookies:"); + for (Cookie cookie : wwwCookies) { + log.info(" {}={} (expires: {})", cookie.name(), cookie.value(), cookie.expiresAt()); + } + } + + // Add user.91160.com cookies first (authentication cookies) + if (userCookies != null && !userCookies.isEmpty()) { + for (int i = 0; i < userCookies.size(); i++) { + if (cookieHeader.length() > 0) { + cookieHeader.append("; "); + } + Cookie cookie = userCookies.get(i); + cookieHeader.append(cookie.name()).append("=").append(cookie.value()); + } + } + + // Add www.91160.com cookies (session cookies) + if (wwwCookies != null && !wwwCookies.isEmpty()) { + for (int i = 0; i < wwwCookies.size(); i++) { + if (cookieHeader.length() > 0) { + cookieHeader.append("; "); + } + Cookie cookie = wwwCookies.get(i); + cookieHeader.append(cookie.name()).append("=").append(cookie.value()); + } + } + + // 只有在提交请求时才使用默认Cookie补全 + if (isSubmitRequest) { + log.info("提交请求Cookie数量不足,使用默认Cookie补全"); + + // 如果user.91160.com的Cookie为空,添加默认Cookie + if (userCookies == null || userCookies.isEmpty()) { + //TODO 应该从用户配置中获取 + List defaultUserCookies = DefaultCookieConfig.getDefaultUserCookies(); + CookieStore.put("user.91160.com", defaultUserCookies); + log.info("已添加默认user.91160.com Cookie: {} 个", defaultUserCookies.size()); + + // 添加到请求头 + for (Cookie cookie : defaultUserCookies) { + if (cookieHeader.length() > 0) { + cookieHeader.append("; "); + } + cookieHeader.append(cookie.name()).append("=").append(cookie.value()); + } + } + + // 如果www.91160.com的Cookie为空,添加默认Cookie + if (wwwCookies == null || wwwCookies.isEmpty()) { + List defaultWwwCookies = DefaultCookieConfig.getDefaultWwwCookies(); + CookieStore.put("www.91160.com", defaultWwwCookies); + log.info("已添加默认www.91160.com Cookie: {} 个", defaultWwwCookies.size()); + + // 添加到请求头 + for (Cookie cookie : defaultWwwCookies) { + if (cookieHeader.length() > 0) { + cookieHeader.append("; "); + } + cookieHeader.append(cookie.name()).append("=").append(cookie.value()); + } + } + } + + if (cookieHeader.length() > 0) { + log.debug("Final cookie header: {}", cookieHeader.toString()); + // //mock it + // String mockCookieHeader = ""; + requestBuilder.addHeader("Cookie", cookieHeader.toString()); + } else { + log.warn("No cookies found for domain: {}", url.host()); + } + } + + // 为提交请求设置请求头 + if (isSubmitRequest) { + // 从请求体中提取参数来构建正确的 Referer URL + String unitId = "131"; + String depId = "369"; + String scheduleId = "68e9933a283d48ea65e075c5"; + + okhttp3.RequestBody requestBody = request.body(); + if (requestBody != null) { + try { + okio.Buffer buffer = new okio.Buffer(); + requestBody.writeTo(buffer); + String bodyString = buffer.readUtf8(); + + // 从请求体中提取参数 + unitId = extractParamFromBody(bodyString, "unit_id"); + depId = extractParamFromBody(bodyString, "dep_id"); + scheduleId = extractParamFromBody(bodyString, "schedule_id"); + + // 重新创建请求体,因为我们已经读取了它 + requestBuilder.method(request.method(), + okhttp3.RequestBody.create(requestBody.contentType(), bodyString)); + } catch (Exception e) { + log.warn("Failed to extract params from request body: {}", e.getMessage()); + } + } + + // 构建正确的 Referer URL + String refererUrl = String.format("%s/guahao/ystep1/uid-%s/depid-%s/schid-%s.html", + SystemConstant.DOMAIN, unitId, depId, scheduleId); + log.info("Referer URL: {}", refererUrl); + requestBuilder.addHeader("User-Agent", SystemConstant.DEFECT_USER_AGENT) + //TODO 对于通道1和通道2,这里的referer是否兼容? + .addHeader("Referer", refererUrl) + .addHeader("Origin", SystemConstant.DOMAIN) + .addHeader("Connection", "keep-alive") + .addHeader("Pragma", "no-cache") + .addHeader("Cache-Control", "no-cache"); + // .addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7") + // .addHeader("Accept-Encoding", "gzip, deflate, br, zstd") + // .addHeader("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8") + // .addHeader("Cache-Control", "no-cache") + // .addHeader("Pragma", "no-cache") + // .addHeader("Sec-Ch-Ua", "\"Chromium\";v=\"140\", \"Not=A?Brand\";v=\"24\", \"Google Chrome\";v=\"140\"") + // .addHeader("Sec-Ch-Ua-Mobile", "0") + // .addHeader("Sec-Ch-Ua-Platform", "\"Windows\"") + // .addHeader("Sec-Fetch-Dest", "document") + // .addHeader("Sec-Fetch-Mode", "navigate") + // .addHeader("Sec-Fetch-Site", "same-origin") + // .addHeader("Sec-Fetch-User", "1") + // .addHeader("Upgrade-Insecure-Requests", "1") + // .addHeader("Priority", "u=0, i"); + } else { + requestBuilder.addHeader("User-Agent", SystemConstant.DEFECT_USER_AGENT) + .addHeader("Referer", SystemConstant.DOMAIN) + .addHeader("Origin", SystemConstant.DOMAIN) + .addHeader("Connection", "keep-alive") + .addHeader("Pragma", "no-cache") + .addHeader("Cache-Control", "no-cache"); + } + Request newReq = requestBuilder.build(); return chain.proceed(newReq); } + + /** + * 从请求体中提取参数值 + */ + private String extractParamFromBody(String bodyString, String paramName) { + try { + String[] pairs = bodyString.split("&"); + for (String pair : pairs) { + if (pair.startsWith(paramName + "=")) { + return pair.substring((paramName + "=").length()); + } + } + } catch (Exception e) { + log.warn("Failed to extract param {} from body: {}", paramName, e.getMessage()); + } + return ""; // 如果找不到参数,返回空字符串 + } } From 53d0e70c168144d9e928dcd59bb19c766d26de96 Mon Sep 17 00:00:00 2001 From: wufei-png Date: Sun, 12 Oct 2025 02:26:37 +0800 Subject: [PATCH 6/7] chore: update comments --- src/main/java/com/github/pengpan/cmd/Register.java | 2 +- src/main/java/com/github/pengpan/entity/Config.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/pengpan/cmd/Register.java b/src/main/java/com/github/pengpan/cmd/Register.java index a5da96d..112696a 100644 --- a/src/main/java/com/github/pengpan/cmd/Register.java +++ b/src/main/java/com/github/pengpan/cmd/Register.java @@ -184,7 +184,7 @@ private void checkBasicConfig(Config config) { Assert.notEmpty(config.getDoctorId(), "[doctorId]不能为空,请检查配置文件"); Assert.notEmpty(config.getWeeks(), "[weeks]不能为空,请检查配置文件"); Assert.notEmpty(config.getDays(), "[days]不能为空,请检查配置文件"); - Assert.isTrue(config.getRandomSleepTime() >= 0, "[sleepTime]格式不正确,请检查配置文件, 例如 \"sleepTime=3000-8000\" "); + Assert.isTrue(config.getRandomSleepTime() >= 0, "[sleepTime]格式不正确,请检查配置文件, 例如 \"sleepTime=1000-2000 或者\" sleepTime=1000\" "); if (StrUtil.isNotBlank(config.getBrushStartDate())) { Date brushStartDate = CommonUtil.parseDate(config.getBrushStartDate(), DatePattern.NORM_DATE_PATTERN); Assert.notNull(brushStartDate, "[brushStartDate]格式不正确,请检查配置文件"); diff --git a/src/main/java/com/github/pengpan/entity/Config.java b/src/main/java/com/github/pengpan/entity/Config.java index 905602e..188af80 100644 --- a/src/main/java/com/github/pengpan/entity/Config.java +++ b/src/main/java/com/github/pengpan/entity/Config.java @@ -140,7 +140,7 @@ public class Config { /** * 获取随机休眠时间(毫秒) - * 支持格式: "3000" 或 "3000-8000" + * 支持格式: "3000" 或 "3000-8000", 如果为3000则是固定的 * @return 随机休眠时间 */ public int getRandomSleepTime() { From 2e6794d20fbc88f3cd01b03b67ca0594cde9d3c3 Mon Sep 17 00:00:00 2001 From: wufei-png Date: Sun, 12 Oct 2025 02:27:20 +0800 Subject: [PATCH 7/7] feat: add build script --- README.md | 13 +++++++++++++ build.sh | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100755 build.sh diff --git a/README.md b/README.md index 8cdd9e6..ceca667 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,19 @@ proxyMode=ROUND_ROBIN 2. 执行jar时加入`-Dfile.encoding=utf-8`,即`java -Dfile.encoding=utf-8 -jar 91160-cli.jar init|register`; 3. 终端不要用`powershell`,要用`cmd.exe`; +## Development 开发者 + +### 如何编译 + +```bash +./build.sh +``` +随后运行 +``` +java -jar target/91160-cli-jar-with-dependencies.jar register -c config.properties +``` + + ## 赞赏作者 如果您觉得`91160-cli`对你有帮助,可以请作者喝杯咖啡哦~ diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..ab47005 --- /dev/null +++ b/build.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +# Simple Build Script for 91160-cli +# This script provides a simpler way to build the application without complex assembly + +set -e # Exit on any error + +echo "=== 91160-cli Simple Build Script ===" +echo "Current directory: $(pwd)" + +# Check if we're in the right directory +if [ ! -f "pom.xml" ]; then + echo "Error: pom.xml not found. Please run this script from the project root directory." + exit 1 +fi + +# Check Java version +echo "Checking Java version..." +java -version + +# Check if Maven is available +if ! command -v mvn &> /dev/null; then + echo "Maven not found. Installing Maven..." + + # Try to install Maven using package manager + if command -v apt-get &> /dev/null; then + echo "Installing Maven via apt-get..." + sudo apt-get update && sudo apt-get install -y maven + elif command -v yum &> /dev/null; then + echo "Installing Maven via yum..." + sudo yum install -y maven + elif command -v brew &> /dev/null; then + echo "Installing Maven via brew..." + brew install maven + else + echo "Error: Cannot install Maven automatically. Please install Maven manually." + echo "Download from: https://maven.apache.org/download.cgi" + exit 1 + fi +fi + +echo "Maven version:" +mvn --version + +# Clean previous builds +echo "Cleaning previous builds..." +mvn clean + +# Compile and package (this will create the jar-with-dependencies) +echo "Compiling and packaging..." +mvn package + +# Check if build was successful +if [ -f "target/91160-cli-jar-with-dependencies.jar" ]; then + echo "✅ Build successful!" + echo "JAR file created: target/91160-cli-jar-with-dependencies.jar" \ No newline at end of file