use super::RuntimeBot;
use crate::bot::BotInformation;
use crate::error::BotError;
use crate::event::id::ID;
use crate::plugin::PluginInfo;
use crate::types::ApiAndOptOneshot;
use crate::{Bot, PluginBuilder};
use parking_lot::RwLock;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::mpsc;
#[cfg(feature = "plugin-access-control")]
pub use crate::plugin::SetAccessControlList;
#[cfg(feature = "plugin-access-control")]
use ahash::HashSet;
#[cfg(feature = "plugin-access-control")]
use serde::{Deserialize, Serialize};
#[deprecated(since = "0.11.0", note = "弃用,直接删掉就好了")]
pub trait KoviApi {}
#[derive(Debug, Clone)]
pub enum SetAdmin {
Add(ID),
Adds(Vec<ID>),
Remove(ID),
Removes(Vec<ID>),
Changes(Vec<ID>),
}
#[cfg(feature = "plugin-access-control")]
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct AccessList {
pub friends: HashSet<ID>,
pub groups: HashSet<ID>,
}
#[cfg(feature = "plugin-access-control")]
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub enum AccessControlMode {
BlackList,
WhiteList,
}
#[cfg(feature = "plugin-access-control")]
impl RuntimeBot {
pub fn set_plugin_access_control<T: AsRef<str>>(
&self,
plugin_name: T,
enable: bool,
) -> Result<(), BotError> {
let bot = match self.bot.upgrade() {
Some(b) => b,
None => return Err(BotError::RefExpired),
};
let mut bot = bot.write();
let plugin_name = plugin_name.as_ref();
let plugin = match bot.plugins.get_mut(plugin_name) {
Some(v) => v,
None => return Err(BotError::PluginNotFound(plugin_name.to_string())),
};
plugin.set_access_control(enable);
Ok(())
}
pub fn set_plugin_access_control_mode<T: AsRef<str>>(
&self,
plugin_name: T,
access_control_mode: AccessControlMode,
) -> Result<(), BotError> {
let bot = match self.bot.upgrade() {
Some(b) => b,
None => return Err(BotError::RefExpired),
};
let mut bot = bot.write();
let plugin_name = plugin_name.as_ref();
let plugin = match bot.plugins.get_mut(plugin_name) {
Some(v) => v,
None => return Err(BotError::PluginNotFound(plugin_name.to_string())),
};
plugin.set_access_control_mode(access_control_mode);
Ok(())
}
pub fn set_plugin_access_control_list<T: AsRef<str>>(
&self,
plugin_name: T,
is_group: bool,
change: SetAccessControlList,
) -> Result<(), BotError> {
let bot = match self.bot.upgrade() {
Some(b) => b,
None => return Err(BotError::RefExpired),
};
let mut bot = bot.write();
let plugin_name = plugin_name.as_ref();
let plugin = match bot.plugins.get_mut(plugin_name) {
Some(v) => v,
None => return Err(BotError::PluginNotFound(plugin_name.to_string())),
};
plugin.set_access_control_list(is_group, change);
Ok(())
}
}
impl RuntimeBot {
pub fn set_deputy_admins(&self, change: SetAdmin) -> Result<(), BotError> {
let bot = match self.bot.upgrade() {
Some(b) => b,
None => return Err(BotError::RefExpired),
};
let bot = bot.read();
let mut bot_info_lock = bot.information.write();
let main_admin = bot_info_lock.get_main_admin().clone();
let mut deputy_admins = bot_info_lock.get_deputy_admins().clone();
match change {
SetAdmin::Add(id) => {
deputy_admins.insert(id);
}
SetAdmin::Adds(ids) => {
deputy_admins.extend(ids);
}
SetAdmin::Remove(id) => {
deputy_admins.remove(&id);
}
SetAdmin::Removes(ids) => {
deputy_admins.retain(|x| !ids.contains(x));
}
SetAdmin::Changes(ids) => {
deputy_admins = ids.into_iter().collect();
}
}
*bot_info_lock = BotInformation::build(main_admin, deputy_admins);
Ok(())
}
pub fn get_main_admin(&self) -> Result<ID, BotError> {
let bot = match self.bot.upgrade() {
Some(b) => b,
None => return Err(BotError::RefExpired),
};
let id = bot.read().information.read().get_main_admin().clone();
Ok(id)
}
pub fn get_deputy_admins(&self) -> Result<Vec<ID>, BotError> {
let bot = match self.bot.upgrade() {
Some(b) => b,
None => return Err(BotError::RefExpired),
};
let ids = bot.read().information.read().get_deputy_admins().clone();
Ok(ids.into_iter().collect())
}
pub fn get_all_admin(&self) -> Result<Vec<ID>, BotError> {
let bot = match self.bot.upgrade() {
Some(b) => b,
None => return Err(BotError::RefExpired),
};
let mut admins = Vec::with_capacity(1);
let bot = bot.read();
admins.push(bot.information.read().get_main_admin().clone());
admins.extend(bot.information.read().get_deputy_admins().clone());
Ok(admins)
}
}
impl RuntimeBot {
pub fn get_data_path(&self) -> PathBuf {
let data_root_path = crate::utils::get_data_root_path();
data_root_path.join(self.plugin_name.clone())
}
}
impl RuntimeBot {
pub fn get_plugin_info(&self) -> Result<Vec<PluginInfo>, BotError> {
let bot = match self.bot.upgrade() {
Some(b) => b,
None => return Err(BotError::RefExpired),
};
let bot = bot.read();
let plugins_info: Vec<PluginInfo> = bot
.plugins
.iter()
.map(|(name, plugin)| PluginInfo {
name: name.clone(),
version: plugin.version.clone(),
enabled: *plugin.enabled.borrow(),
enable_on_startup: plugin.enable_on_startup,
#[cfg(feature = "plugin-access-control")]
access_control: plugin.access_control,
#[cfg(feature = "plugin-access-control")]
list_mode: plugin.list_mode,
#[cfg(feature = "plugin-access-control")]
access_list: plugin.access_list.clone(),
})
.collect();
Ok(plugins_info)
}
pub async fn restart_plugin<T: AsRef<str>>(&self, plugin_name: T) -> Result<(), BotError> {
if self.is_plugin_enable(&plugin_name)? {
let join = self.disable_plugin(&plugin_name)?;
if let Some(join) = join {
join.await.expect("Internal thread panic")
}
}
self.enable_plugin(plugin_name)
}
pub fn disable_plugin<T: AsRef<str>>(
&self,
plugin_name: T,
) -> Result<Option<tokio::task::JoinHandle<()>>, BotError> {
if !self.is_plugin_enable(&plugin_name)? {
return Ok(None);
}
let bot = match self.bot.upgrade() {
Some(b) => b,
None => return Err(BotError::RefExpired),
};
Ok(Some(disable_plugin(bot, plugin_name)?))
}
pub fn enable_plugin<T: AsRef<str>>(&self, plugin_name: T) -> Result<(), BotError> {
if self.is_plugin_enable(&plugin_name)? {
return Ok(());
}
let bot = match self.bot.upgrade() {
Some(b) => b,
None => return Err(BotError::RefExpired),
};
enable_plugin(bot, plugin_name, self.api_tx.clone())
}
pub fn is_plugin_enable<T: AsRef<str>>(&self, plugin_name: T) -> Result<bool, BotError> {
let bot = match self.bot.upgrade() {
Some(b) => b,
None => return Err(BotError::RefExpired),
};
let bot = bot.read();
let plugin_name = plugin_name.as_ref();
let bot_plugin = match bot.plugins.get(plugin_name) {
Some(v) => v,
None => return Err(BotError::PluginNotFound(plugin_name.to_string())),
};
let bool_ = *bot_plugin.enabled.borrow();
Ok(bool_)
}
}
pub(crate) fn disable_plugin<T: AsRef<str>>(
bot: Arc<RwLock<Bot>>,
plugin_name: T,
) -> Result<tokio::task::JoinHandle<()>, BotError> {
let join;
{
let mut bot = bot.write();
let plugin_name = plugin_name.as_ref();
let bot_plugin = match bot.plugins.get_mut(plugin_name) {
Some(v) => v,
None => return Err(BotError::PluginNotFound(plugin_name.to_string())),
};
join = bot_plugin.shutdown();
}
Ok(join)
}
fn enable_plugin<T: AsRef<str>>(
bot: Arc<RwLock<Bot>>,
plugin_name: T,
api_tx: mpsc::Sender<ApiAndOptOneshot>,
) -> Result<(), BotError> {
let bot_read = bot.read();
let plugin_name = plugin_name.as_ref();
let Some(bot_plugin) = bot_read.plugins.get(plugin_name) else {
return Err(BotError::PluginNotFound(plugin_name.to_string()));
};
bot_plugin.enabled.send_modify(|v| {
*v = true;
});
let plugin_ = bot_plugin.clone();
let plugin_builder = PluginBuilder::new(plugin_name.to_string(), bot.clone(), api_tx);
tokio::spawn(async move { plugin_.run(plugin_builder) });
Ok(())
}