use std::io;
use std::path::Path;
use std::time::Duration;
use rand::{thread_rng, Rng};
use ricq::{LoginResponse, RQError};
use tokio::fs;
use tokio::io::AsyncWriteExt;
use tracing::{error, info, warn};
use crate::bot::BotConfiguration;
use crate::config::login::{LoginConfig, DEFAULT_CONFIG};
use crate::{config, get_app, Bot};
pub async fn login_bots() -> Result<(), RQError> {
let login_conf_dir = {
let path = config::service_config_dir_path();
if !path.is_dir() {
fs::create_dir_all(&path).await?;
}
path
}
.join("login.toml");
let login_conf = {
async fn default_config_write<P: AsRef<Path>>(path: P) -> io::Result<LoginConfig> {
fs::write(path, DEFAULT_CONFIG).await?;
let default_config = LoginConfig::default();
Ok(default_config)
}
if login_conf_dir.is_file() {
let s = fs::read_to_string(&login_conf_dir).await?;
match toml::from_str(&s) {
Ok(conf) => conf,
Err(e) => {
error!("读取登陆配置文件失败: {}", e);
let cp = config::service_config_dir_path().join("login.toml.bak");
fs::copy(&login_conf_dir, cp).await?;
default_config_write(&login_conf_dir).await?
}
}
} else {
default_config_write(login_conf_dir).await?
}
};
let bots_path = config::bots_dir_path();
if !bots_path.is_dir() {
fs::create_dir(&bots_path).await?;
}
let mut logins = vec![];
for bot in login_conf.bots {
if !bot.auto_login {
continue;
}
let account = bot.account;
let pwd = bot.password;
let bot_path = bots_path.join(account.to_string()).join("device.json");
if !bot_path.is_file() {
warn!("未找到Bot({})的登陆信息,跳过登陆", account);
continue;
}
let handle = tokio::spawn(async move {
match login_bot(
account,
&pwd,
BotConfiguration {
work_dir: None,
version: bot
.protocol
.unwrap_or(login_conf.default_protocol)
.as_version(),
},
)
.await
{
Ok(bot) => {
if let Err(e) = bot.refresh_friend_list().await {
warn!("{}刷新好友列表失败: {:?}", bot, e);
}
if let Err(e) = bot.refresh_group_list().await {
warn!("{}刷新群列表失败: {:?}", bot, e);
}
Ok(bot)
}
Err(e) => {
get_app().remove_bot(account);
Err(e)
}
}
});
logins.push(handle);
let random = { thread_rng().gen_range(0..44) as f32 / 11.2f32 };
tokio::time::sleep(Duration::from_secs_f32(random)).await;
}
for result in logins {
match result.await.expect("Login panics!") {
Ok(_) => {}
Err(_) => {}
}
}
Ok(())
}
async fn login_bot(
account: i64,
password: &Option<String>,
conf: BotConfiguration,
) -> Result<Bot, RQError> {
let bot = Bot::new(account, conf).await;
get_app().add_bot(bot.clone());
bot.start().await?;
info!("Bot({})登陆中", account);
match bot.try_login().await {
Ok(_) => {
info!("{}登陆成功", bot);
Ok(bot)
}
Err(e) => {
if let Some(pwd) = password {
info!("{}尝试密码登陆", bot);
let mut resp = bot.client().password_login(account, pwd).await?;
loop {
match resp {
LoginResponse::DeviceLockLogin(..) => {
resp = bot.client().device_lock_login().await?;
}
LoginResponse::Success(..) => {
info!("{}登陆成功", bot);
let mut dir = bot.work_dir();
dir.push("token.json");
if let Ok(mut f) = fs::File::create(&dir).await {
let token = bot.client().gen_token().await;
let s = serde_json::to_string_pretty(&token)
.expect("Cannot serialize token");
let _ = f.write_all(s.as_bytes()).await;
}
break;
}
LoginResponse::UnknownStatus(ref s) => {
error!("{}登陆失败: {}", bot, s.message);
return Err(e);
}
LoginResponse::AccountFrozen => {
error!("{}登陆失败: 账号被冻结", bot);
return Err(e);
}
or => {
error!("{}登陆失败, 服务器返回: {:?}", bot, or);
return Err(e);
}
}
}
Ok(bot)
} else {
error!("{}登陆失败: {:?}", bot, e);
Err(e)
}
}
}
}