mirror of
http://shenjack.top:5100/shenjack/icalingua-python-bot.git
synced 2025-02-23 01:09:58 +08:00
能跑了吗?
This commit is contained in:
parent
16fee092ba
commit
fcf88f0ebb
|
@ -13,7 +13,7 @@ use crate::error::{ClientResult, IcaError};
|
|||
use crate::{version_str, StopGetter};
|
||||
|
||||
/// icalingua 客户端的兼容版本号
|
||||
pub const ICA_PROTOCOL_VERSION: &str = "2.12.24";
|
||||
pub const ICA_PROTOCOL_VERSION: &str = "2.12.26";
|
||||
|
||||
mod status {
|
||||
use crate::data_struct::ica::all_rooms::Room;
|
||||
|
|
|
@ -7,7 +7,7 @@ use colored::Colorize;
|
|||
use ed25519_dalek::{Signature, Signer, SigningKey};
|
||||
use rust_socketio::asynchronous::Client;
|
||||
use rust_socketio::Payload;
|
||||
use serde_json::{Value, json};
|
||||
use serde_json::{json, Value};
|
||||
use tracing::{debug, event, span, warn, Level};
|
||||
|
||||
/// "安全" 的 发送一条消息
|
||||
|
@ -130,9 +130,7 @@ pub async fn send_room_sign_in(client: &Client, room_id: RoomId) -> bool {
|
|||
/// 向某个群/私聊的某个人发送戳一戳
|
||||
pub async fn send_poke(client: &Client, room_id: RoomId, target: UserId) -> bool {
|
||||
let data = json!([room_id, target]);
|
||||
match client.emit(
|
||||
"sendGroupPoke", data
|
||||
).await {
|
||||
match client.emit("sendGroupPoke", data).await {
|
||||
Ok(_) => {
|
||||
event!(Level::INFO, "已向 {} 的 {} 发送戳一戳", room_id, target);
|
||||
true
|
||||
|
|
|
@ -72,8 +72,7 @@ pub async fn add_message(payload: Payload, client: Client) {
|
|||
if message.content().starts_with(&format!("/bot-enable-{}", client_id)) {
|
||||
// 尝试获取后面的信息
|
||||
if let Some((_, name)) = message.content().split_once(" ") {
|
||||
let path_name = PathBuf::from(name);
|
||||
match py::PyStatus::get().get_status(&path_name) {
|
||||
match py::PyStatus::get().get_status(name) {
|
||||
None => {
|
||||
let reply = message.reply_with("未找到插件");
|
||||
send_message(&client, &reply).await;
|
||||
|
@ -83,7 +82,7 @@ pub async fn add_message(payload: Payload, client: Client) {
|
|||
send_message(&client, &reply).await;
|
||||
}
|
||||
Some(false) => {
|
||||
py::PyStatus::get_mut().set_status(&path_name, true);
|
||||
py::PyStatus::get_mut().set_status(name, true);
|
||||
let reply = message.reply_with("启用插件完成");
|
||||
send_message(&client, &reply).await;
|
||||
}
|
||||
|
@ -92,8 +91,7 @@ pub async fn add_message(payload: Payload, client: Client) {
|
|||
} else if message.content().starts_with(&format!("/bot-disable-{}", client_id))
|
||||
{
|
||||
if let Some((_, name)) = message.content().split_once(" ") {
|
||||
let path_name = PathBuf::from(name);
|
||||
match py::PyStatus::get().get_status(&path_name) {
|
||||
match py::PyStatus::get().get_status(name) {
|
||||
None => {
|
||||
let reply = message.reply_with("未找到插件");
|
||||
send_message(&client, &reply).await;
|
||||
|
@ -103,7 +101,7 @@ pub async fn add_message(payload: Payload, client: Client) {
|
|||
send_message(&client, &reply).await;
|
||||
}
|
||||
Some(true) => {
|
||||
py::PyStatus::get_mut().set_status(&path_name, false);
|
||||
py::PyStatus::get_mut().set_status(name, false);
|
||||
let reply = message.reply_with("禁用插件完成");
|
||||
send_message(&client, &reply).await;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use toml_edit::{value, DocumentMut, Key, Table, TomlError, Value};
|
|||
use tracing::{event, Level};
|
||||
|
||||
use crate::py::PyStatus;
|
||||
use crate::MainStatus;
|
||||
|
||||
/// ```toml
|
||||
/// # 这个文件是由 shenbot 自动生成的, 请 **谨慎** 修改
|
||||
|
@ -31,14 +32,37 @@ pub const DEFAULT_CONFIG: &str = r#"
|
|||
|
||||
impl PluginConfigFile {
|
||||
pub fn from_str(data: &str) -> Result<Self, TomlError> {
|
||||
let data = DocumentMut::from_str(data)?;
|
||||
let mut data = DocumentMut::from_str(data)?;
|
||||
if data.get(CONFIG_KEY).is_none() {
|
||||
event!(Level::WARN, "插件配置文件缺少 plugins 字段, 正在初始化");
|
||||
data.insert_formatted(
|
||||
&Key::from_str(CONFIG_KEY).unwrap(),
|
||||
toml_edit::Item::Table(Table::new()),
|
||||
);
|
||||
}
|
||||
Ok(Self { data })
|
||||
}
|
||||
|
||||
pub fn default_init() -> anyhow::Result<Self> {
|
||||
let config_path = MainStatus::global_config().py().config_path.clone();
|
||||
let path = Path::new(&config_path);
|
||||
Self::from_config_path(&path)
|
||||
}
|
||||
|
||||
pub fn reload_from_default(&mut self) {
|
||||
let new_config = Self::default_init();
|
||||
if let Err(e) = new_config {
|
||||
event!(Level::ERROR, "从配置文件重加载时遇到错误: {}", e);
|
||||
return;
|
||||
}
|
||||
let new_config = new_config.unwrap();
|
||||
self.data = new_config.data;
|
||||
}
|
||||
|
||||
pub fn from_config_path(path: &Path) -> anyhow::Result<Self> {
|
||||
let config_path = path.join(CONFIG_FILE_NAME);
|
||||
if !config_path.exists() {
|
||||
event!(Level::INFO, "插件配置文件不存在, 正在创建");
|
||||
event!(Level::WARN, "插件配置文件不存在, 正在创建");
|
||||
std::fs::write(&config_path, DEFAULT_CONFIG)?;
|
||||
Ok(Self::from_str(DEFAULT_CONFIG)?)
|
||||
} else {
|
||||
|
@ -47,16 +71,6 @@ impl PluginConfigFile {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn verify_and_init(&mut self) {
|
||||
if self.data.get(CONFIG_KEY).is_none() {
|
||||
event!(Level::INFO, "插件配置文件缺少 plugins 字段, 正在初始化");
|
||||
self.data.insert_formatted(
|
||||
&Key::from_str(CONFIG_KEY).unwrap(),
|
||||
toml_edit::Item::Table(Table::new()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_table(&self) -> Option<&Table> {
|
||||
self.data.get(CONFIG_KEY).and_then(|item| item.as_table())
|
||||
}
|
||||
|
@ -67,11 +81,10 @@ impl PluginConfigFile {
|
|||
|
||||
/// 获取插件状态
|
||||
/// 默认为 true
|
||||
pub fn get_status(&self, path: &Path) -> bool {
|
||||
let path_str = path.to_str().unwrap();
|
||||
pub fn get_status(&self, plugin_id: &str) -> bool {
|
||||
if let Some(item) = self.data.get(CONFIG_KEY) {
|
||||
if let Some(table) = item.as_table() {
|
||||
if let Some(item) = table.get(path_str) {
|
||||
if let Some(item) = table.get(plugin_id) {
|
||||
if let Some(bool) = item.as_bool() {
|
||||
return bool;
|
||||
}
|
||||
|
@ -100,7 +113,6 @@ impl PluginConfigFile {
|
|||
|
||||
/// 设置插件状态
|
||||
pub fn set_status(&mut self, path: &Path, status: bool) {
|
||||
self.verify_and_init();
|
||||
let path_str = path.to_str().unwrap();
|
||||
let table = self.data.get_mut(CONFIG_KEY).unwrap().as_table_mut().unwrap();
|
||||
if table.contains_key(path_str) {
|
||||
|
@ -115,23 +127,31 @@ impl PluginConfigFile {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn sync_status_from_config(&mut self) {
|
||||
pub fn read_status_from_file(&mut self) {
|
||||
self.reload_from_default();
|
||||
event!(Level::INFO, "同步插件状态");
|
||||
let plugins = PyStatus::get_mut();
|
||||
self.verify_and_init();
|
||||
plugins.files.iter_mut().for_each(|(path, status)| {
|
||||
let config_status = self.get_status(path);
|
||||
event!(Level::INFO, "插件状态: {:?} {} -> {}", path, status.enabled, config_status);
|
||||
let plugin_id = status.get_id();
|
||||
let config_status = self.get_status(&plugin_id);
|
||||
event!(
|
||||
Level::INFO,
|
||||
"插件状态: {}({:?}) {} -> {}",
|
||||
status.get_id(),
|
||||
path,
|
||||
status.enabled,
|
||||
config_status
|
||||
);
|
||||
status.enabled = config_status;
|
||||
});
|
||||
}
|
||||
|
||||
pub fn sync_status_to_config(&mut self) {
|
||||
let plugins = PyStatus::get();
|
||||
self.verify_and_init();
|
||||
let table = self.data.get_mut(CONFIG_KEY).unwrap().as_table_mut().unwrap();
|
||||
table.clear();
|
||||
plugins.files.iter().for_each(|(path, status)| {
|
||||
table.insert(path.to_str().unwrap(), value(status.enabled));
|
||||
plugins.files.iter().for_each(|(_, status)| {
|
||||
table.insert(&status.get_id(), value(status.enabled));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -29,10 +29,8 @@ static mut PyPluginStatus: OnceLock<PyStatus> = OnceLock::new();
|
|||
|
||||
impl PyStatus {
|
||||
pub fn init() {
|
||||
let plugin_path = MainStatus::global_config().py().plugin_path.clone();
|
||||
let mut config =
|
||||
config::PluginConfigFile::from_config_path(&PathBuf::from(plugin_path)).unwrap();
|
||||
config.verify_and_init();
|
||||
let config =
|
||||
config::PluginConfigFile::default_init().expect("初始化 Python 插件配置文件失败");
|
||||
let status = PyStatus {
|
||||
files: HashMap::new(),
|
||||
config,
|
||||
|
@ -44,37 +42,40 @@ impl PyStatus {
|
|||
|
||||
pub fn get_mut() -> &'static mut PyStatus { unsafe { PyPluginStatus.get_mut().unwrap() } }
|
||||
|
||||
/// 添加一个插件
|
||||
pub fn add_file(&mut self, path: PathBuf, plugin: PyPlugin) { self.files.insert(path, plugin); }
|
||||
|
||||
/// 删除一个插件
|
||||
pub fn delete_file(&mut self, path: &PathBuf) -> Option<PyPlugin> { self.files.remove(path) }
|
||||
|
||||
pub fn get_status(&self, pluging_id: &str) -> Option<bool> {
|
||||
self.files.iter().find_map(|(_, plugin)| {
|
||||
if plugin.get_id() == pluging_id {
|
||||
return Some(plugin.enabled);
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_status(&mut self, pluging_id: &str, status: bool) {
|
||||
self.files.iter_mut().for_each(|(_, plugin)| {
|
||||
if plugin.get_id() == pluging_id {
|
||||
plugin.enabled = status;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn verify_file(&self, path: &PathBuf) -> bool {
|
||||
self.files.get(path).is_some_and(|plugin| plugin.verifiy())
|
||||
}
|
||||
|
||||
/// 获取某个插件的状态
|
||||
/// 以 config 优先
|
||||
pub fn get_status(&self, path: &PathBuf) -> Option<bool> {
|
||||
self.files.get(path).map(|plugin| plugin.enabled)
|
||||
}
|
||||
|
||||
pub fn sync_status(&mut self) { self.config.sync_status_from_config(); }
|
||||
|
||||
pub fn set_status(&mut self, path: &Path, status: bool) {
|
||||
self.config.set_status(path, status);
|
||||
if let Some(plugin) = self.files.get_mut(path) {
|
||||
plugin.enabled = status;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display() -> String {
|
||||
format!(
|
||||
"Python 插件 {{ {} }}",
|
||||
Self::get()
|
||||
.files
|
||||
.iter()
|
||||
.map(|(k, v)| format!("{:?}-{}", k, v.enabled))
|
||||
.map(|(_, v)| v.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n")
|
||||
)
|
||||
|
@ -133,7 +134,7 @@ impl PyPlugin {
|
|||
Ok(plugin) => {
|
||||
self.py_module = plugin.py_module;
|
||||
self.changed_time = plugin.changed_time;
|
||||
self.enabled = PyStatus::get().config.get_status(self.file_path.as_path());
|
||||
self.enabled = PyStatus::get().config.get_status(&self.get_id());
|
||||
event!(Level::INFO, "更新 Python 插件文件 {:?} 完成", self.file_path);
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -164,6 +165,14 @@ impl PyPlugin {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> String { plugin_path_as_id(&self.file_path) }
|
||||
}
|
||||
|
||||
impl ToString for PyPlugin {
|
||||
fn to_string(&self) -> String {
|
||||
format!("{}({:?})-{}", self.get_id(), self.file_path, self.enabled)
|
||||
}
|
||||
}
|
||||
|
||||
pub const CONFIG_DATA_NAME: &str = "CONFIG_DATA";
|
||||
|
@ -304,6 +313,15 @@ impl TryFrom<RawPyPlugin> for PyPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
/// 插件路径转换为 id
|
||||
pub fn plugin_path_as_id(path: &PathBuf) -> String {
|
||||
path.file_name()
|
||||
.unwrap_or_default()
|
||||
.to_str()
|
||||
.unwrap_or("decode-failed")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn load_py_plugins(path: &PathBuf) {
|
||||
let plugins = PyStatus::get_mut();
|
||||
if path.exists() {
|
||||
|
@ -330,7 +348,7 @@ pub fn load_py_plugins(path: &PathBuf) {
|
|||
} else {
|
||||
event!(Level::WARN, "插件加载目录不存在: {:?}", path);
|
||||
}
|
||||
plugins.config.sync_status_from_config();
|
||||
plugins.config.read_status_from_file();
|
||||
event!(
|
||||
Level::INFO,
|
||||
"python 插件目录: {:?} 加载完成, 加载到 {} 个插件",
|
||||
|
|
|
@ -115,8 +115,7 @@ pub async fn on_message(payload: Payload, client: Client, _status: Arc<BotStatus
|
|||
// 先判定是否为 admin
|
||||
// 尝试获取后面的信息
|
||||
if let Some((_, name)) = message.content.split_once(" ") {
|
||||
let path_name = PathBuf::from(name);
|
||||
match PyStatus::get().get_status(&path_name) {
|
||||
match PyStatus::get().get_status(name) {
|
||||
None => {
|
||||
let reply = message.reply_with("未找到插件");
|
||||
send_message(&client, &reply).await;
|
||||
|
@ -126,7 +125,7 @@ pub async fn on_message(payload: Payload, client: Client, _status: Arc<BotStatus
|
|||
send_message(&client, &reply).await;
|
||||
}
|
||||
Some(false) => {
|
||||
PyStatus::get_mut().set_status(&path_name, true);
|
||||
PyStatus::get_mut().set_status(name, true);
|
||||
let reply = message.reply_with("启用插件完成");
|
||||
send_message(&client, &reply).await;
|
||||
}
|
||||
|
@ -134,8 +133,7 @@ pub async fn on_message(payload: Payload, client: Client, _status: Arc<BotStatus
|
|||
}
|
||||
} else if message.content.starts_with(&format!("/bot-disable-{}", client_id)) {
|
||||
if let Some((_, name)) = message.content.split_once(" ") {
|
||||
let path_name = PathBuf::from(name);
|
||||
match PyStatus::get().get_status(&path_name) {
|
||||
match PyStatus::get().get_status(name) {
|
||||
None => {
|
||||
let reply = message.reply_with("未找到插件");
|
||||
send_message(&client, &reply).await;
|
||||
|
@ -145,7 +143,7 @@ pub async fn on_message(payload: Payload, client: Client, _status: Arc<BotStatus
|
|||
send_message(&client, &reply).await;
|
||||
}
|
||||
Some(true) => {
|
||||
PyStatus::get_mut().set_status(&path_name, false);
|
||||
PyStatus::get_mut().set_status(name, false);
|
||||
let reply = message.reply_with("禁用插件完成");
|
||||
send_message(&client, &reply).await;
|
||||
}
|
||||
|
|
4
news.md
4
news.md
|
@ -2,9 +2,10 @@
|
|||
|
||||
## 0.8.0
|
||||
|
||||
- ica 兼容版本号更新到 `2.12.24`
|
||||
- ica 兼容版本号更新到 ~~`2.12.24`~~ `2.12.26`
|
||||
- 从 `py::PyStatus` 开始进行一个 `static mut` -> `static mut OnceLock` 的改造
|
||||
- 用于看着更舒服(逃)
|
||||
- 部分重构了一下 读取插件启用状态 的配置文件的代码
|
||||
|
||||
### ica 1.6.5
|
||||
|
||||
|
@ -136,6 +137,7 @@
|
|||
好耶!
|
||||
|
||||
[!note]
|
||||
|
||||
```text
|
||||
notice_room = []
|
||||
notice_start = true
|
||||
|
|
Loading…
Reference in New Issue
Block a user