最近上班闲来无事,每天都在混掘金,看文章学技术。每天签到混点矿石,然后我就突然想到,那我为什么不实现一个自动签到及抽奖的脚本呢?反正闲着也是闲着,还不如干点喜欢的事。
说干就干,最近在研究学习rust语言。那我就用rust实现一个简单的掘金自动签到及抽奖的脚本。
首先,我们需要准备好要用到的rust工具库:
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
简单介绍一下:
- tokio,大名鼎鼎的rust异步运行时框架。用来实现async\await异步运行。
- serde,序列化与反序列化工具库。
- reqwest,http请求库。
准备好工具,我们立马开始正文。
1. 定义全局变量
通过const关键字,定义接口为全局变量,用字符串字面量把请求接口写进程序的硬编码。注意类型为&str。
签到抽奖需要的接口:
//基础url
pub const BASE_URL: &str = "https://api.juejin.cn";
//查询今日是否已经签到
pub const ISSIGNINURL: &str = "/growth_api/v1/get_today_status";
//签到
pub const SIGNINURL: &str = "/growth_api/v1/check_in";
//查询今日免费抽奖机会
pub const ISDRAW: &str = "/growth_api/v1/lottery_config/get";
//抽奖
pub const DRAWURL: &str = "/growth_api/v1/lottery/draw";
记录账号需要的cookie信息:
pub const AID: &str = "xxxx";
pub const UUID: &str = "xxxxx";
pub const SIGNATURE: &str = "xxx";
pub const _COOKIE: &str="xxxxx";
2.初始化reqwest客户端
首先定义好通用的错误类型:
type RespErr = Box<dyn std::error::Error>;
然后初始化reqwest客户端:
use reqwest::header::{self, COOKIE};
use reqwest::Client;
#[derive(Serialize, Deserialize, Debug)]
pub struct Post<'a> {
pub aid: &'a str,
pub uuid: &'a str,
pub _signature: &'a str,
pub cookie: &'a str,
}
#[tokio::main]
async fn main() -> Result<(), RespErr> {
let params = Post {
aid: AID,
uuid: UUID,
_signature: SIGNATURE,
cookie: _COOKIE,
};
let client = init(¶ms).unwrap();
Ok(())
}
这里定义一个post结构体,把前面写好的账号cookie信息当作请求头和请求参数。注意post的生命周期标识'a,因为前面我们把cookie信息写进了全局变量,所以post结构体是引用了全局变量的值,并不拥有所有权。在结构体中使用引用需要为结构体中的每一个引用标注上生命周期。
下面是初始化reqwest客户端函数:
//初始化reqwest客户端
fn init(params: &Post) -> Result<Client, RespErr> {
let mut headers = header::HeaderMap::new();
headers.insert(COOKIE, params.cookie.parse().unwrap());
let client = Client::builder().default_headers(headers).build()?;
Ok(client)
}
在HTTP请求头加上cookie信息,初始化reqwest客户端之后将其返回。
3. 检验是否已签到
首先是检验是否已签到的函数:
#[derive(Debug, Deserialize)]
pub struct SignResp {
pub err_no: i32,
pub err_msg: String,
pub data: Option<bool>,
}
//是否已签到
async fn is_sign_in(client: Client) -> Result<SignResp, RespErr> {
let resp = client
.get(BASE_URL.to_string() + ISSIGNINURL)
.send()
.await?
.json::<SignResp>()
.await?;
println!("是否已签到:{:#?}", resp.data.unwrap());
Ok(resp)
}
把reqwest客户端当作参数,实现HTTP请求。定义SignResp结构体作为返回值。在这里用json::<SignResp>()方法对返回值进行json序列化。
然后在mian函数里面使用is_sign_in函数:
#[tokio::main]
async fn main() -> Result<(), RespErr> {
let params = Post {
aid: AID,
uuid: UUID,
_signature: SIGNATURE,
cookie: _COOKIE,
};
let client = init(¶ms).unwrap();
//加上此内容
let sign_resp = is_sign_in(client.clone()).await?;
if let Some(false) = sign_resp.data {
sign_in(client.clone(), ¶ms).await?;
};
Ok(())
}
因为事先用Option<bool>把返回值的data进行了包裹,进行json序列化之后,会成Some(T)或者None枚举(至于为什么需要用Option进行包裹?你猜?)。然后对返回值的data进行模式匹配,匹配到Some(false)表示未签到。
随后运行签到的sign_in方法。
//签到
async fn sign_in(client: Client, new_post: &Post<'_>) -> Result<SignResp, RespErr> {
let resp = client
.post(BASE_URL.to_string() + SIGNINURL)
.json(new_post)
.send()
.await?
.json::<SignResp>()
.await?;
println!("签到:{:#?}", resp);
Ok(resp)
}
这时,返回值的data会变成null,所以SignResp结构体的data类型需要是Option<bool>(bingo,你猜中了没)。经过进行json序列化之后,会成None。
4. 检测是否已抽奖
经过前面的签到,我们会获得一次免费的抽奖机会。
在签到之后,继续进行抽奖进程:
if let Some(false) = sign_resp.data {
sign_in(client.clone(), ¶ms).await?;
let draw_resp = is_draw(client.clone()).await?;
if draw_resp.data.free_count != 0 {
draw(client.clone(), ¶ms).await?;
}
};
//是否已抽奖
async fn is_draw(client: Client) -> Result<DrawResp, RespErr> {
let resp = client
.get(BASE_URL.to_string() + ISDRAW)
.send()
.await?
.json::<DrawResp>()
.await?;
println!("未抽奖次数还有{:#?}次", resp.data.free_count);
Ok(resp)
}
因为返回值的结构,所以需要定义多层嵌套的DrawResp结构体:
#[derive(Debug, Deserialize)]
pub struct DrawResp {
pub err_no: i8,
pub err_msg: String,
pub data: DrawData,
}
#[derive(Debug, Deserialize)]
pub struct DrawData {
pub lottery: Vec<Lottery>,
pub free_count: i8,
pub point_cost: i32,
}
#[derive(Debug, Deserialize)]
pub struct Lottery {
pub lottery_id: String,
pub lottery_name: String,
pub lottery_type: i8,
pub lottery_image: String,
pub unlock_count: i8,
}
当返回值的free_count不为0时,表示还剩下免费抽奖次数。
继续进行抽奖:
//抽奖
async fn draw(client: Client, new_post: &Post<'_>) -> Result<String, RespErr> {
let resp = client
.post(BASE_URL.to_string() + DRAWURL)
.json(new_post)
.send()
.await?
.text()
.await?;
println!("抽奖:{:#?}", resp);
Ok(resp)
}
5. 结束
就这样,我用rust实现了一个简简单单的掘金签到+抽奖脚本。
项目在这里:github
如果大佬们有更好的处理方法,可以提pr。
注:本项目仅当学习用途,各位切莫以身犯险。容易被封号!