背景
自从掘金的签到抽奖让小海尝到点甜头后(实际上是没有什么鸟用的桌垫加徽章),小海便加入了每日攒矿石的抽奖大队,结果一个月前连续签到二十天断了,又回到解放前的100→200矿石的日子,于是乎痛定思痛决定把事情交给程序来做,javaer就该拿程序说话
开始行动
首先我们新建一个springboot项目,为什么要用springbooot呢?因为小海是一年经验的java后端,技术栈就是ctrl c,v +springboot。好了然后我们加上@EnableScheduling 开启定时任务,每天执行我们的抽奖代码。再加上@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}) 取消默认的datasource配置,因为我们并不需要数据库支持,这样我们的架子就搭好了,开始写业务。
/**
* @author lizhihai
* @date 2021/09/11
* 开启定时任务,取消springdatasource加载
*/
@EnableScheduling
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
public class MyTestProApplication {
public static void main(String[] args) {
SpringApplication.run(MyTestProApplication.class, args);
}
}
复制代码
签到抽奖请求
掘金的签到抽奖这几个请求都是https协议的,有证书的请求,所以我们多的不说,用HttpURLConnection写个通用的发送https请求的方法,和获取返回值的方法,这里参考www.jianshu.com/p/848911df8…
/**
* 通用https请求方法 掘金是https协议有证书请求
*
* @param url
* @param method
* @return
* @throws Exception
*/
public String commnoReuqest(String url, String method,Cookies cookie) throws Exception {
URL serverUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();
conn.setRequestMethod(method);
conn.setRequestProperty("Content-type", "application/json");
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36");
conn.setRequestProperty("Cookie", cookie.getValue());
//必须设置false,否则会自动redirect到重定向后的地址
conn.setInstanceFollowRedirects(false);
conn.connect();
String result = getReturn(conn);
return result;
}
/*请求url获取返回的内容*/
public static String getReturn(HttpURLConnection connection) throws IOException {
StringBuffer buffer = new StringBuffer();
//将返回的输入流转换成字符串
try (InputStream inputStream = connection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);) {
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
String result = buffer.toString();
return result;
}
}
复制代码
掘金api
然后我们就打开熟悉的F12看看掘金签到抽奖的请求是咋样的,搜索api,根据url猜猜各个请求都是干啥的,下面我们把整理好的api都写到常量类里面,并且把cookie 复制下来,根据各个url和通用请求方法,封装好签到,抽奖,查询矿石的方法。拿到cookie,拿到url,现在我们已经可以在程序里去执行签到抽奖的操作了,写个test试一下, 请求得到结果{“err_no”:15001,”err_msg”:”您今日已完成签到,请勿重复签到”,”data”:null}
,ok 测试结果成功get√
public class Constans {
public static final String POST = "POST";
public static final String GET = "GET";
//URL
//签到
public static final String CHECK_IN = "https://api.juejin.cn/growth_api/v1/check_in";
//抽奖
public static final String DRAW = "https://api.juejin.cn/growth_api/v1/lottery/draw";
//查询剩余矿石
public static final String GET_CUR_POINT = "https://api.juejin.cn/growth_api/v1/get_cur_point";
}
复制代码
/**
* 签到
*
* @throws Exception
*/
public boolean checkIn(Cookies cookie) throws Exception {
String response = commnoReuqest(Constans.CHECK_IN, Constans.POST,cookie);
logger.info("{}{}",cookie.getName(),response);
boolean hasSign=!response.contains("err_no":0");
if(hasSign){
sendHttps.sendToDingDing(cookie.getName()+response);
}
return hasSign;
}
/**
* 抽奖
*
* @throws Exception
*/
public void draw(Cookies cookie) throws Exception {
String response = commnoReuqest(Constans.DRAW, Constans.POST,cookie);
DrawResponce data= JSON.parseObject(response,new TypeReference<DrawResponce>(){});
String prizeName=data.getData().getLotteryName();
if(!"66矿石".equals(prizeName)&&!prizeName.contains("Bug")){
log.error("{}抽到好东西了{}!!!!!!,cookie.getName()+"抽到"+prizeName");
}
logger.info(response);
}
复制代码
扩展性
这里小海拉了开了个小号并且拉了个小伙伴加入抽奖大队,所以需要完成批量抽奖操作,并且考虑到有的小伙伴希望每天使用免费抽奖,攒着矿石,有的希望每天梭哈抽奖,所以我们定义一个枚举类,属性有name,value,allin,分别代表小伙伴的名字,小伙伴的掘金cookie,小伙伴是否希望梭哈抽奖。这样我们就能以遍历枚举值的方式去批量抽奖了
//cookie 掘金的cookie据说有一个月有效期,挂了就再拷一个过来
public enum Cookies {
HAI1(true,"小海183","_ga=GA1.2.1296772436.1629686778; passport_csrf_token_default=f933ed7f9d24bfb06894114528a64af6; passport_csrf_token=f933ed7f9d24bfb06894114528a64af6; MONITOR_WEB_ID=aa936331-59e1-4607-8edf-84657bbc22cd; _gid=GA1.2.1269321208.1630996120; odin_tt=1bb7f242d137177d63dd9302d566ca5eac4c9fcb56b1dde948623c0efaf17cbecade967d875212a51b1c0e85f5643f2b08907c58b7b23435c0f866d83a18cf6e; passport_auth_status=0481add371299d2704126689380b9849%2C703a60ebb0e24b3a72db9bdc379e399d; passport_auth_status_ss=0481add371299d2704126689380b9849%2C703a60ebb0e24b3a72db9bdc379e399d; sid_guard=a6220a3e86f808bb214cbbe6d482a529%7C1631266482%7C5184000%7CTue%2C+09-Nov-2021+09%3A34%3A42+GMT; uid_tt=ccb35b269c8a75cb1dbd63c6d37a8a53; uid_tt_ss=ccb35b269c8a75cb1dbd63c6d37a8a53; sid_tt=a6220a3e86f808bb214cbbe6d482a529; sessionid=a6220a3e86f808bb214cbbe6d482a529; sessionid_ss=a6220a3e86f808bb214cbbe6d482a529; sid_ucp_v1=1.0.0-KDZkMGY3MDVmMzIyODI0MzU3Y2IyOWRjNGFmOGUxYTQwMGIwNTA3MWMKFwi93KCx4o2gBRCyzeyJBhiwFDgCQPEHGgJsZiIgYTYyMjBhM2U4NmY4MDhiYjIxNGNiYmU2ZDQ4MmE1Mjk; ssid_ucp_v1=1.0.0-KDZkMGY3MDVmMzIyODI0MzU3Y2IyOWRjNGFmOGUxYTQwMGIwNTA3MWMKFwi93KCx4o2gBRCyzeyJBhiwFDgCQPEHGgJsZiIgYTYyMjBhM2U4NmY4MDhiYjIxNGNiYmU2ZDQ4MmE1Mjk; n_mh=KF_8KclpLr_zR7cAEjb3RtZNBW4-HCCsXj4xdrmiLXE"),
HAI2(true,"小海176","MONITOR_WEB_ID=46e596ce-9ecc-4a89-992a-7c6a6e0c3481; _ga=GA1.2.1129485207.1631332210; _gid=GA1.2.1280463638.1631332210; passport_csrf_token_default=79180cb48bf4906a01362c777619a03e; passport_csrf_token=79180cb48bf4906a01362c777619a03e; passport_auth_status=26b44631a00ffda2ca525997e425471b%2C; passport_auth_status_ss=26b44631a00ffda2ca525997e425471b%2C; sid_guard=da5b5f8fa2be84a74656f9ed8f0f58de%7C1631332246%7C5184000%7CWed%2C+10-Nov-2021+03%3A50%3A46+GMT; uid_tt=369af9b9a6bd0a458166702eb1a9eb76; uid_tt_ss=369af9b9a6bd0a458166702eb1a9eb76; sid_tt=da5b5f8fa2be84a74656f9ed8f0f58de; sessionid=da5b5f8fa2be84a74656f9ed8f0f58de; sessionid_ss=da5b5f8fa2be84a74656f9ed8f0f58de; sid_ucp_v1=1.0.0-KDA2ODRmYTJmODZiOThjYjBjNDhjYjZjYzMwZjY0NzNhNzM1ODdhYmIKFwjnkrCFu4yWAxCWz_CJBhiwFDgCQPEHGgJsZiIgZGE1YjVmOGZhMmJlODRhNzQ2NTZmOWVkOGYwZjU4ZGU; ssid_ucp_v1=1.0.0-KDA2ODRmYTJmODZiOThjYjBjNDhjYjZjYzMwZjY0NzNhNzM1ODdhYmIKFwjnkrCFu4yWAxCWz_CJBhiwFDgCQPEHGgJsZiIgZGE1YjVmOGZhMmJlODRhNzQ2NTZmOWVkOGYwZjU4ZGU; n_mh=gY-EUdc0-atNYE3DFtWTsH7CHt0JMgKQkRnBimbpdn8; _gat=1"),
WU(true,"小吴","MONITOR_WEB_ID=da85199d-f4d8-434b-a709-6470a7f2ae08; _ga=GA1.2.1545343137.1631330098; _gid=GA1.2.1996029296.1631330098; passport_csrf_token_default=c95cd45eb0563c42a850d971f8f8d670; passport_csrf_token=c95cd45eb0563c42a850d971f8f8d670; passport_auth_status=46573bcc67d38f6c4202117b42f9b248%2C; passport_auth_status_ss=46573bcc67d38f6c4202117b42f9b248%2C; sid_guard=ed395050035826d1ea12953bd5728b05%7C1631330346%7C5184000%7CWed%2C+10-Nov-2021+03%3A19%3A06+GMT; uid_tt=4f23e928a63e23958189356b7349ddc1; uid_tt_ss=4f23e928a63e23958189356b7349ddc1; sid_tt=ed395050035826d1ea12953bd5728b05; sessionid=ed395050035826d1ea12953bd5728b05; sessionid_ss=ed395050035826d1ea12953bd5728b05; sid_ucp_v1=1.0.0-KDAzNjEzZDNkM2FmODRhNDIxY2E3ODQ4YzJkYjY4MDI5MzczNmFjZDYKFwjnr4CNvIyqBRCqwPCJBhiwFDgCQPEHGgJsZiIgZWQzOTUwNTAwMzU4MjZkMWVhMTI5NTNiZDU3MjhiMDU; ssid_ucp_v1=1.0.0-KDAzNjEzZDNkM2FmODRhNDIxY2E3ODQ4YzJkYjY4MDI5MzczNmFjZDYKFwjnr4CNvIyqBRCqwPCJBhiwFDgCQPEHGgJsZiIgZWQzOTUwNTAwMzU4MjZkMWVhMTI5NTNiZDU3MjhiMDU; n_mh=fCN8FMn4hIUuqN_fJo4Lf9qCwiFWUh01_NgRv1iwdfQ");
private String name;//名字
private String value;//cookie,自己上浏览器粘贴过来
private boolean isAllIn;//是否梭哈
Cookies(boolean isAllIn,String name, String value) {
this.isAllIn=isAllIn;
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public boolean isAllIn() {
return isAllIn;
}
public String getValue() {
return value;
}
}
复制代码
定时任务
重点来了,最后一步抽奖定时任务,大家看代码吧,到这里我们就可以不用担心周末断签,从而解放双手卸载掘金app了。。嘿嘿嘿
@Scheduled(cron = "0 1 0 * * ?")
public void excute() throws Exception {
logger.info("定时任务开始");
for(Cookies cookie:Cookies.values()){
boolean hasSign=checkIn(cookie);
if(hasSign) {continue;}
// 第一次免费抽奖
draw(cookie);
//还能抽就继续干
while(cookie.isAllIn()&&getCurPoint(cookie)>=200){
draw(cookie);
}
}
logger.info("执行结束");
}
复制代码
顺带一提
这里掘金api的返回一层包一层,使用maputils十分繁琐,给大家推荐一款idea插件 gsonformat 直接把请求返回的字符串拷贝下来一键生成我们的抽奖结果响应类,美滋滋。
@NoArgsConstructor
@Data
public class DrawResponce {
@JsonProperty("err_no")
private Integer errNo;
@JsonProperty("err_msg")
private String errMsg;
@JsonProperty("data")
private DataDTO data;
@NoArgsConstructor
@Data
public static class DataDTO {
@JsonProperty("id")
private Integer id;
@JsonProperty("lottery_id")
private String lotteryId;
@JsonProperty("lottery_name")
private String lotteryName;
@JsonProperty("lottery_type")
private Integer lotteryType;
@JsonProperty("lottery_image")
private String lotteryImage;
@JsonProperty("history_id")
private String historyId;
}
}
复制代码
钉钉机器人
到这里有小伙伴就问了,我想知道我抽到啥怎么办,难不成跑到你公司看你的控制台搜log.info?于是我参考(ctrl c+v)这两篇文章www.jb51.cc/java/526453… ,www.cnblogs.com/fugitive/p/… 对接钉钉,代码就不贴了,挺多的,对接我自己的钉钉群机器人(ip验证的方式) 然后我每天十二点都能收到若干条钉钉消息:小海抽到66矿石。就挺突然的,哭了
最后
最后:这是菜鸡小java小海第一次写文章,有很多祖传的ctrl c v操作,大佬们看个乐就行。源码已经上传到github,github.com/NicholasZhi… 嫌麻烦想白嫖抽奖的可以评论区按格式发送你的 name,allin,cookie。例如(尼古拉斯小海,梭哈/不梭哈,”nicholas”)我拉你进群加入抽奖大队哦