kovi-plugin-like 4.0.0

kovi 的点赞插件,可在config.json里配置。
Documentation
use kovi::event::MessageEventTrait;
use kovi::event::id::ID;
use kovi::log::info;
pub use kovi::tokio;
use kovi::utils::{load_json_data, save_json_data};
use kovi::{PluginBuilder as P, RuntimeBot};
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::time::{SystemTime, UNIX_EPOCH};

#[cfg(not(any(feature = "onebot", feature = "milky")))]
compile_error!("请至少启用一个协议 feature: \"onebot\"\"milky\"");

#[cfg(all(feature = "onebot", feature = "milky"))]
compile_error!("不能同时启用 onebot 和 milky feature");

#[cfg(feature = "onebot")]
use kovi_onebot::*;

#[cfg(feature = "milky")]
use kovi_milky::*;

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Config {
    today: Vec<ID>,
    /// 储存秒级别时间戳
    data_time: u64,
    like_times: usize,
    msg: Msg,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct Msg {
    cmd: String,
    like: String,
    today: String,
    do_not_like_you: String,
}

#[kovi::plugin]
async fn main() {
    let bot = P::get_runtime_bot();
    let mut path = bot.get_data_path();
    path.push("config.json");

    let config: Config = {
        let config: Config = serde_json::from_value(json!({
            "today": [],
            "data_time": 1,
            "like_times": 10,
            "msg": {
                "cmd": "赞我",
                "like": "已为你点赞10次",
                "today": "今天赞过了,一边呆着去!",
                "do_not_like_you": "就不给你点,略略略"
            }
        }))
        .unwrap();

        let mut config: Config = match load_json_data(config.clone(), &path) {
            Ok(v) => v,
            Err(e) => {
                // 是json解析报错的话?
                if let Some(_parse_error) = e.downcast_ref::<serde_json::Error>() {
                    save_json_data(&config, &path).unwrap();
                    config
                } else {
                    panic!("{e}")
                }
            }
        };
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();
        if config.data_time / 86400 != now / 86400 {
            config.today = Vec::new();
            config.data_time = now;
            save_json_data(&config, &path).unwrap();
        }
        config
    };

    let msg = Arc::new(config.msg.clone());
    let path = Arc::new(path);
    let config_arc = Arc::new(Mutex::new(config));

    use kovi::async_move;

    P::on(async_move!(e; msg, bot, config_arc, path;  {
        on_msg_send_like(e, msg, bot, config_arc, path).await
    }));

    // P::on({
    //     let msg = msg.clone();
    //     let bot = bot.clone();
    //     let config_arc = config_arc.clone();
    //     let path = path.clone();
    //     move |e: Arc<MsgEvent>| {
    //         on_msg_send_like(
    //             e,
    //             msg.clone(),
    //             bot.clone(),
    //             config_arc.clone(),
    //             path.clone(),
    //         )
    //     }
    // });

    P::cron("0 0 * * *", move || {
        let config_clone_for_reset = config_arc.clone();
        let path = path.clone();
        async move {
            // 清空 `today`
            let mut config = config_clone_for_reset.lock().unwrap();
            config.today.clear();
            config.data_time = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .unwrap()
                .as_secs();
            info!("like插件正在清理");
            save_json_data(&*config, &*path).unwrap();
        }
    })
    .unwrap();
}

async fn on_msg_send_like(
    e: Arc<MsgEvent>,
    msg: Arc<Msg>,
    bot: Arc<RuntimeBot>,
    config_arc: Arc<Mutex<Config>>,
    path: Arc<PathBuf>,
) {
    if e.borrow_text() != Some(&msg.cmd) {
        return;
    }

    {
        let config_lock = config_arc.lock().unwrap();

        if config_lock.today.contains(&e.get_sender_id().to_id()) {
            e.reply_and_quote(&msg.today);
            return;
        }
    }

    #[cfg(feature = "onebot")]
    let res = bot
        .send_like_return(*(e.get_sender_id().try_as_i64_or_panic()), 10)
        .await;

    #[cfg(feature = "milky")]
    let res = {
        use kovi::bot::runtimebot::CanSendApi as _;

        bot.send_api_return(
            "send_profile_like",
            json!({
                "user_id": e.get_sender_id().try_as_i64_or_panic(),
                "count": 10
            }),
        )
        .await
    };

    {
        let mut config_lock = config_arc.lock().unwrap();

        match res {
            Ok(_) => {
                e.reply_and_quote(&msg.like);
                config_lock.today.push(e.get_sender_id().to_id());
                save_json_data(&*config_lock, &*path).unwrap();
            }
            Err(_) => e.reply_and_quote(&msg.do_not_like_you),
        };
    }
}