use anyhow::Result;
use bytes::Bytes;
use ricq::client::{Connector as _, DefaultConnector};
use ricq::ext::common::after_login;
use ricq::handler::DefaultHandler;
use ricq::handler::{Handler, QEvent};
use ricq::{Client, Device, Protocol};
use ricq::{LoginResponse, QRCodeConfirmed, QRCodeImageFetch, QRCodeState};
use std::path::Path;
use std::sync::Arc;
use tokio::time::{sleep, Duration};
use tracing::Level;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
tracing_subscriber::registry()
.with(tracing_subscriber::fmt::layer().with_target(true))
.with(
tracing_subscriber::filter::Targets::new()
.with_target("ricq", Level::DEBUG)
.with_target("qrcode_login", Level::DEBUG),
)
.init();
let device = match Path::new("device.json").exists() {
true => serde_json::from_str(&tokio::fs::read_to_string("device.json").await?)?,
false => {
let d = Device::random();
tokio::fs::write("device.json", serde_json::to_string(&d).unwrap()).await?;
d
}
};
let client = Arc::new(Client::new(
device,
Protocol::AndroidWatch.into(),
MyHandler,
));
let handle = tokio::spawn({
let client = client.clone();
let stream = DefaultConnector.connect(&client).await.unwrap();
async move { client.start(stream).await }
});
tokio::task::yield_now().await;
let mut resp = client.fetch_qrcode().await?;
let mut image_sig = Bytes::new();
loop {
match resp {
QRCodeState::ImageFetch(QRCodeImageFetch {
ref image_data,
ref sig,
}) => {
tokio::fs::write("qrcode.png", &image_data).await?;
image_sig = sig.clone();
tracing::info!("二维码: qrcode.png");
}
QRCodeState::WaitingForScan => {
tracing::info!("二维码待扫描")
}
QRCodeState::WaitingForConfirm => {
tracing::info!("二维码待确认")
}
QRCodeState::Timeout => {
tracing::info!("二维码已超时,重新获取");
if let QRCodeState::ImageFetch(QRCodeImageFetch {
ref image_data,
ref sig,
}) = client.fetch_qrcode().await?
{
tokio::fs::write("qrcode.png", &image_data)
.await
.expect("failed to write file");
image_sig = sig.clone();
tracing::info!("二维码: qrcode.png");
}
}
QRCodeState::Confirmed(QRCodeConfirmed {
ref tmp_pwd,
ref tmp_no_pic_sig,
ref tgt_qr,
..
}) => {
tracing::info!("二维码已确认");
let mut login_resp = client.qrcode_login(tmp_pwd, tmp_no_pic_sig, tgt_qr).await?;
if let LoginResponse::DeviceLockLogin { .. } = login_resp {
login_resp = client.device_lock_login().await?;
}
tracing::info!("{:?}", login_resp);
break;
}
QRCodeState::Canceled => {
panic!("二维码已取消")
}
}
sleep(Duration::from_secs(5)).await;
resp = client
.query_qrcode_result(&image_sig)
.await
.expect("failed to query qrcode result");
}
after_login(&client).await;
handle.await.unwrap();
Ok(())
}
pub struct MyHandler;
#[async_trait::async_trait]
impl Handler for MyHandler {
async fn handle(&self, e: QEvent) {
match e {
QEvent::GroupMessage(m) => {
tracing::info!(
"MESSAGE (GROUP={}): {}",
m.inner.group_code,
m.inner.elements
)
}
QEvent::FriendMessage(m) => {
tracing::info!(
"MESSAGE (FRIEND={}): {}",
m.inner.from_uin,
m.inner.elements
)
}
QEvent::GroupMessageRecall(e) => {
tracing::info!("GroupMessageRecall {:?}", e);
}
QEvent::FriendMessageRecall(e) => {
tracing::info!("FriendMessageRecall {:?}", e);
}
// _ => {}
_ => tracing::info!("{:?}", e),
}
}
}
[package]
name = "qrcode_login"
version = "0.1.0"
edition = "2021"
[dependencies]
ricq = "0.1.17"
tokio = { version = "1", features = ["full"] }
anyhow = "1"
async-trait = "*"
serde_json = "1"
bytes = "1"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["fmt"] }