kovi_plugin_copycat/
lib.rs

1mod config;
2
3use kovi::{
4    PluginBuilder as plugin, RuntimeBot, bot::runtimebot::kovi_api::SetAccessControlList,
5    event::GroupMsgEvent, log::info, tokio::sync::Mutex,
6};
7use std::{
8    collections::HashMap,
9    sync::{Arc, OnceLock},
10};
11
12use crate::config::CONFIG;
13
14static BOT: OnceLock<Arc<RuntimeBot>> = OnceLock::new();
15static STATE: OnceLock<Arc<Mutex<HashMap<i64, State>>>> = OnceLock::new();
16
17struct State {
18    text: String,
19    count: u32,
20    sender: i64,
21}
22
23#[kovi::plugin]
24async fn init() {
25    let bot = BOT.get_or_init(|| plugin::get_runtime_bot());
26    let config = config::init(bot).await.unwrap();
27
28    STATE.get_or_init(|| Arc::new(Mutex::new(HashMap::new())));
29
30    match &config.allow_groups {
31        Some(groups) => {
32            bot.set_plugin_access_control("copycat", true).unwrap();
33            bot.set_plugin_access_control_list(
34                "copycat",
35                true,
36                SetAccessControlList::Adds(groups.clone()),
37            )
38            .unwrap();
39        }
40        None => {
41            bot.set_plugin_access_control("copycat", false).unwrap();
42        }
43    }
44
45    plugin::on_group_msg(on_group_msg);
46}
47
48async fn on_group_msg(event: Arc<GroupMsgEvent>) {
49    let state = STATE.get().unwrap().clone();
50    let config = CONFIG.get().unwrap();
51
52    let msgs = event.message.get("text");
53    if msgs.len() > 1
54        || msgs.is_empty()
55        || !event.message.clone().into_iter().all(|m| m.type_ == "text")
56    {
57        info!("[copycat] Not a plain text message, ignored.");
58        return;
59    }
60
61    let msg = msgs[0].data["text"].as_str().unwrap().to_string();
62
63    {
64        let mut s = state.lock().await;
65        let s = match s.get_mut(&event.group_id) {
66            Some(s) => s,
67            None => {
68                s.insert(
69                    event.group_id,
70                    State {
71                        text: String::new(),
72                        count: 0,
73                        sender: -1,
74                    },
75                );
76                s.get_mut(&event.group_id).unwrap()
77            }
78        };
79
80        if s.text == msg {
81            if s.sender != event.sender.user_id {
82                s.count += 1;
83                info!(
84                    "[copycat] Received repeated message, current count: {} / {}",
85                    s.count, config.repeat_after
86                );
87            }
88        } else {
89            s.text = msg;
90            s.count = 1;
91            s.sender = event.sender.user_id;
92        }
93
94        if s.count == config.repeat_after {
95            BOT.get().unwrap().send_group_msg(event.group_id, &s.text);
96            s.count += 1;
97
98            info!(
99                "[copycat] Meow! Current count: {} / {}",
100                s.count, config.repeat_after
101            );
102        }
103    }
104}