mirror of
https://github.com/TriM-Organization/LiteyukiBot-TriM.git
synced 2026-06-14 00:22:26 +00:00
Ⓜ️手动从旧梦 81a191f merge
This commit is contained in:
+13
-8
@@ -13,7 +13,6 @@ import requests
|
||||
|
||||
from src.utils.base.config import load_from_yaml, config
|
||||
from src.utils.base.log import init_log
|
||||
from src.utils.base.data_manager import TempConfig, auto_migrate, common_db
|
||||
from git import Repo
|
||||
|
||||
|
||||
@@ -53,11 +52,10 @@ def init():
|
||||
|
||||
"""
|
||||
# 检测python版本是否高于3.10
|
||||
auto_migrate()
|
||||
init_log()
|
||||
if sys.version_info < (3, 10):
|
||||
nonebot.logger.error(
|
||||
"此应用需要 Python3.10 以上的版本运行,你需要更新自己的 Python 运行环境了。"
|
||||
"此应用需要 Python3.10 以上的版本运行,你需要抱怨神羽的 Python 兼容性了。"
|
||||
)
|
||||
exit(1)
|
||||
|
||||
@@ -69,9 +67,9 @@ def init():
|
||||
f"无法读取 Git 仓库 {e},你是否是从仓库内下载的Zip文件?请使用git clone。"
|
||||
)
|
||||
|
||||
temp_data: TempConfig = common_db.where_one(TempConfig(), default=TempConfig()) # type: ignore
|
||||
temp_data.data["start_time"] = time.time()
|
||||
common_db.save(temp_data)
|
||||
# temp_data: TempConfig = common_db.where_one(TempConfig(), default=TempConfig())
|
||||
# temp_data.data["start_time"] = time.time()
|
||||
# common_db.save(temp_data)
|
||||
|
||||
# 在加载完成语言后再初始化日志
|
||||
nonebot.logger.info("尹灵温 正在初始化…")
|
||||
@@ -84,6 +82,13 @@ def init():
|
||||
f.write("[tool.nonebot]\n")
|
||||
|
||||
nonebot.logger.info(
|
||||
f"正在 {sys.executable} Python{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} 上运行 尹灵温"
|
||||
"正在 {} Python{}.{}.{} 上运行 尹灵温".format(
|
||||
sys.executable,
|
||||
sys.version_info.major,
|
||||
sys.version_info.minor,
|
||||
sys.version_info.micro,
|
||||
)
|
||||
)
|
||||
nonebot.logger.info(
|
||||
"{} {}({}) 正在运行".format(__NAME__, __VERSION__, __VERSION_I__)
|
||||
)
|
||||
nonebot.logger.info(f"{__NAME__} {__VERSION__}({__VERSION_I__}) 正在运行")
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
from . import (
|
||||
satori,
|
||||
onebot
|
||||
)
|
||||
|
||||
|
||||
def init(config: dict):
|
||||
onebot.init()
|
||||
satori.init(config)
|
||||
|
||||
|
||||
def register():
|
||||
onebot.register()
|
||||
satori.register()
|
||||
@@ -1,12 +0,0 @@
|
||||
import nonebot
|
||||
from nonebot.adapters.onebot import v11, v12
|
||||
|
||||
|
||||
def init():
|
||||
pass
|
||||
|
||||
|
||||
def register():
|
||||
driver = nonebot.get_driver()
|
||||
driver.register_adapter(v11.Adapter)
|
||||
driver.register_adapter(v12.Adapter)
|
||||
@@ -1,26 +0,0 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
import nonebot
|
||||
from nonebot.adapters import satori
|
||||
|
||||
|
||||
def init(config: dict):
|
||||
if config.get("satori", None) is None:
|
||||
nonebot.logger.info("未查见 Satori 的配置文档,将跳过 Satori 初始化")
|
||||
return None
|
||||
satori_config = config.get("satori")
|
||||
if not satori_config.get("enable", False):
|
||||
nonebot.logger.info("未启用 Satori ,将跳过 Satori 初始化")
|
||||
return None
|
||||
if os.getenv("SATORI_CLIENTS", None) is not None:
|
||||
nonebot.logger.info("Satori 客户端已设入环境变量,跳过此步。")
|
||||
os.environ["SATORI_CLIENTS"] = json.dumps(satori_config.get("hosts", []), ensure_ascii=False)
|
||||
config['satori_clients'] = satori_config.get("hosts", [])
|
||||
return
|
||||
|
||||
|
||||
def register():
|
||||
if os.getenv("SATORI_CLIENTS", None) is not None:
|
||||
driver = nonebot.get_driver()
|
||||
driver.register_adapter(satori.Adapter)
|
||||
@@ -0,0 +1,26 @@
|
||||
import threading
|
||||
|
||||
from nonebot import logger
|
||||
from liteyuki.comm.channel import get_channel
|
||||
|
||||
|
||||
def reload(delay: float = 0.0, receiver: str = "nonebot"):
|
||||
"""
|
||||
重载LiteyukiBot(nonebot)
|
||||
Args:
|
||||
receiver: 指定重载的进程
|
||||
delay:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
chan = get_channel(receiver + "-active")
|
||||
if chan is None:
|
||||
logger.error(f"未见 Channel {receiver}-active 以至无法重载")
|
||||
return
|
||||
|
||||
if delay > 0:
|
||||
threading.Timer(delay, chan.send, args=(1,)).start()
|
||||
return
|
||||
else:
|
||||
chan.send(1)
|
||||
|
||||
@@ -5,8 +5,6 @@ import nonebot
|
||||
import yaml
|
||||
from pydantic import BaseModel
|
||||
|
||||
from .data_manager import StoredConfig, TempConfig, common_db
|
||||
from .ly_typing import T_Bot
|
||||
from ..message.tools import random_hex_string
|
||||
|
||||
config = {} # 全局配置,确保加载后读取
|
||||
@@ -70,9 +68,6 @@ def get_config(key: str, default=None):
|
||||
elif key in config:
|
||||
return config[key]
|
||||
|
||||
elif key in common_db.where_one(StoredConfig(), default=StoredConfig()).config:
|
||||
return common_db.where_one(StoredConfig(), default=StoredConfig()).config[key]
|
||||
|
||||
elif key in load_from_yaml("config.yml"):
|
||||
return load_from_yaml("config.yml")[key]
|
||||
|
||||
@@ -80,11 +75,6 @@ def get_config(key: str, default=None):
|
||||
return default
|
||||
|
||||
|
||||
def set_stored_config(key: str, value):
|
||||
temp_config: TempConfig = common_db.where_one(TempConfig(), default=TempConfig())
|
||||
temp_config.data[key] = value
|
||||
common_db.save(temp_config)
|
||||
|
||||
|
||||
def init_conf(conf: dict) -> dict:
|
||||
"""
|
||||
|
||||
+278
-76
@@ -1,12 +1,12 @@
|
||||
import inspect
|
||||
import os
|
||||
import pickle
|
||||
import sqlite3
|
||||
from types import NoneType
|
||||
from typing import Any, Callable
|
||||
from packaging.version import parse
|
||||
import inspect
|
||||
import nonebot
|
||||
import pydantic
|
||||
|
||||
from nonebot import logger
|
||||
from nonebot.compat import PYDANTIC_V2
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
@@ -15,16 +15,18 @@ class LiteModel(BaseModel):
|
||||
id: int = None
|
||||
|
||||
def dump(self, *args, **kwargs):
|
||||
if parse(pydantic.__version__) < parse("2.0.0"):
|
||||
return self.dict(*args, **kwargs)
|
||||
else:
|
||||
if PYDANTIC_V2:
|
||||
return self.model_dump(*args, **kwargs)
|
||||
else:
|
||||
return self.dict(*args, **kwargs)
|
||||
|
||||
|
||||
class Database:
|
||||
def __init__(self, db_name: str):
|
||||
|
||||
if os.path.dirname(db_name) != "" and not os.path.exists(os.path.dirname(db_name)):
|
||||
if os.path.dirname(db_name) != "" and not os.path.exists(
|
||||
os.path.dirname(db_name)
|
||||
):
|
||||
os.makedirs(os.path.dirname(db_name))
|
||||
|
||||
self.db_name = db_name
|
||||
@@ -32,8 +34,34 @@ class Database:
|
||||
self.cursor = self.conn.cursor()
|
||||
|
||||
self._on_save_callbacks = []
|
||||
self._is_locked = False
|
||||
|
||||
def where_one(self, model: LiteModel, condition: str = "", *args: Any, default: Any = None) -> LiteModel | Any | None:
|
||||
def lock(self):
|
||||
self.cursor.execute("BEGIN TRANSACTION")
|
||||
self._is_locked = True
|
||||
|
||||
def lock_query(self, query: str, *args):
|
||||
"""锁定查询"""
|
||||
self.cursor.execute(query, args).fetchall()
|
||||
|
||||
def lock_model(self, model: LiteModel) -> LiteModel | Any | None:
|
||||
"""锁定行
|
||||
Args:
|
||||
model: 数据模型实例
|
||||
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def unlock(self):
|
||||
self.cursor.execute("COMMIT")
|
||||
self._is_locked = False
|
||||
|
||||
def where_one(
|
||||
self, model: LiteModel, condition: str = "", *args: Any, default: Any = None
|
||||
) -> LiteModel | Any | None:
|
||||
"""查询第一个
|
||||
Args:
|
||||
model: 数据模型实例
|
||||
@@ -47,7 +75,9 @@ class Database:
|
||||
all_results = self.where_all(model, condition, *args)
|
||||
return all_results[0] if all_results else default
|
||||
|
||||
def where_all(self, model: LiteModel, condition: str = "", *args: Any, default: Any = None) -> list[LiteModel | Any] | None:
|
||||
def where_all(
|
||||
self, model: LiteModel, condition: str = "", *args: Any, default: Any = None
|
||||
) -> list[LiteModel | Any] | None:
|
||||
"""查询所有
|
||||
Args:
|
||||
model: 数据模型实例
|
||||
@@ -60,7 +90,9 @@ class Database:
|
||||
"""
|
||||
table_name = model.TABLE_NAME
|
||||
model_type = type(model)
|
||||
nonebot.logger.debug(f"Selecting {model.TABLE_NAME} WHERE {condition.replace('?', '%s') % args}")
|
||||
logger.debug(
|
||||
f"数据库 Selecting {model.TABLE_NAME} WHERE {condition.replace('?', '%s') % args}"
|
||||
)
|
||||
if not table_name:
|
||||
raise ValueError(f"数据模型{model_type.__name__}未提供表名")
|
||||
|
||||
@@ -71,14 +103,19 @@ class Database:
|
||||
# else:
|
||||
# results = self.cursor.execute(f"SELECT * FROM {table_name} {condition}", args).fetchall()
|
||||
if condition:
|
||||
results = self.cursor.execute(f"SELECT * FROM {table_name} WHERE {condition}", args).fetchall()
|
||||
results = self.cursor.execute(
|
||||
f"SELECT * FROM {table_name} WHERE {condition}", args
|
||||
).fetchall()
|
||||
else:
|
||||
results = self.cursor.execute(f"SELECT * FROM {table_name}").fetchall()
|
||||
fields = [description[0] for description in self.cursor.description]
|
||||
if not results:
|
||||
return default
|
||||
else:
|
||||
return [model_type(**self._load(dict(zip(fields, result)))) for result in results]
|
||||
return [
|
||||
model_type(**self._load(dict(zip(fields, result))))
|
||||
for result in results
|
||||
]
|
||||
|
||||
def save(self, *args: LiteModel):
|
||||
"""增/改操作
|
||||
@@ -86,13 +123,20 @@ class Database:
|
||||
*args:
|
||||
Returns:
|
||||
"""
|
||||
table_list = [item[0] for item in self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()]
|
||||
table_list = [
|
||||
item[0]
|
||||
for item in self.cursor.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type='table'"
|
||||
).fetchall()
|
||||
]
|
||||
for model in args:
|
||||
nonebot.logger.debug(f"Upserting {model}")
|
||||
logger.debug(f"数据库 Upserting {model}")
|
||||
if not model.TABLE_NAME:
|
||||
raise ValueError(f"数据模型 {model.__class__.__name__} 未提供表名")
|
||||
elif model.TABLE_NAME not in table_list:
|
||||
raise ValueError(f"数据模型 {model.__class__.__name__} 表 {model.TABLE_NAME} 不存在,请先迁移")
|
||||
raise ValueError(
|
||||
f"数据模型 {model.__class__.__name__} 表 {model.TABLE_NAME} 不存在,请先迁移"
|
||||
)
|
||||
else:
|
||||
self._save(model.dump(by_alias=True))
|
||||
|
||||
@@ -107,11 +151,15 @@ class Database:
|
||||
new_obj = {}
|
||||
for field, value in obj.items():
|
||||
if isinstance(value, self.ITERABLE_TYPE):
|
||||
new_obj[self._get_stored_field_prefix(value) + field] = self._save(value) # self._save(value) # -> bytes
|
||||
new_obj[self._get_stored_field_prefix(value) + field] = self._save(
|
||||
value
|
||||
) # self._save(value) # -> bytes
|
||||
elif isinstance(value, self.BASIC_TYPE):
|
||||
new_obj[field] = value
|
||||
else:
|
||||
raise ValueError(f"数据模型{table_name}包含不支持的数据类型,字段:{field} 值:{value} 值类型:{type(value)}")
|
||||
raise ValueError(
|
||||
f"数据模型{table_name}包含不支持的数据类型,字段:{field} 值:{value} 值类型:{type(value)}"
|
||||
)
|
||||
if table_name:
|
||||
fields, values = [], []
|
||||
for n_field, n_value in new_obj.items():
|
||||
@@ -123,14 +171,23 @@ class Database:
|
||||
values = list(values)
|
||||
if row_id is not None:
|
||||
# 如果 _id 不为空,将 'id' 插入到字段列表的开始
|
||||
fields.insert(0, 'id')
|
||||
fields.insert(0, "id")
|
||||
# 将 _id 插入到值列表的开始
|
||||
values.insert(0, row_id)
|
||||
fields = ', '.join([f'"{field}"' for field in fields])
|
||||
placeholders = ', '.join('?' for _ in values)
|
||||
self.cursor.execute(f"INSERT OR REPLACE INTO {table_name}({fields}) VALUES ({placeholders})", tuple(values))
|
||||
self.conn.commit()
|
||||
foreign_id = self.cursor.execute("SELECT last_insert_rowid()").fetchone()[0]
|
||||
fields = ", ".join([f'"{field}"' for field in fields])
|
||||
placeholders = ", ".join("?" for _ in values)
|
||||
self.cursor.execute(
|
||||
f"INSERT OR REPLACE INTO {table_name}({fields}) VALUES ({placeholders})",
|
||||
tuple(values),
|
||||
)
|
||||
# self.conn.commit()
|
||||
if self._is_locked:
|
||||
pass
|
||||
else:
|
||||
self.conn.commit()
|
||||
foreign_id = self.cursor.execute(
|
||||
"SELECT last_insert_rowid()"
|
||||
).fetchone()[0]
|
||||
return f"{self.FOREIGN_KEY_PREFIX}{foreign_id}@{table_name}" # -> FOREIGN_KEY_123456@{table_name} id@{table_name}
|
||||
else:
|
||||
return pickle.dumps(new_obj) # -> bytes
|
||||
@@ -143,10 +200,14 @@ class Database:
|
||||
elif isinstance(item, self.BASIC_TYPE):
|
||||
new_obj.append(item)
|
||||
else:
|
||||
raise ValueError(f"数据模型包含不支持的数据类型,值:{item} 值类型:{type(item)}")
|
||||
raise ValueError(
|
||||
f"数据模型包含不支持的数据类型,值:{item} 值类型:{type(item)}"
|
||||
)
|
||||
return pickle.dumps(obj_type(new_obj)) # -> bytes
|
||||
else:
|
||||
raise ValueError(f"数据模型包含不支持的数据类型,值:{obj} 值类型:{type(obj)}")
|
||||
raise ValueError(
|
||||
f"数据模型包含不支持的数据类型,值:{obj} 值类型:{type(obj)}"
|
||||
)
|
||||
|
||||
def _load(self, obj: Any) -> Any:
|
||||
|
||||
@@ -160,14 +221,18 @@ class Database:
|
||||
|
||||
if field.startswith(self.BYTES_PREFIX):
|
||||
if isinstance(value, bytes):
|
||||
new_obj[field.replace(self.BYTES_PREFIX, "")] = self._load(pickle.loads(value))
|
||||
new_obj[field.replace(self.BYTES_PREFIX, "")] = self._load(
|
||||
pickle.loads(value)
|
||||
)
|
||||
else: # 从value字段可能为None,fix at 2024/6/13
|
||||
pass
|
||||
# 暂时不作处理,后面再修
|
||||
|
||||
elif field.startswith(self.FOREIGN_KEY_PREFIX):
|
||||
|
||||
new_obj[field.replace(self.FOREIGN_KEY_PREFIX, "")] = self._load(self._get_foreign_data(value))
|
||||
new_obj[field.replace(self.FOREIGN_KEY_PREFIX, "")] = self._load(
|
||||
self._get_foreign_data(value)
|
||||
)
|
||||
|
||||
else:
|
||||
new_obj[field] = value
|
||||
@@ -193,7 +258,9 @@ class Database:
|
||||
else:
|
||||
return obj
|
||||
|
||||
def delete(self, model: LiteModel, condition: str, *args: Any, allow_empty: bool = False):
|
||||
def delete(
|
||||
self, model: LiteModel, condition: str, *args: Any, allow_empty: bool = False
|
||||
):
|
||||
"""
|
||||
删除满足条件的数据
|
||||
Args:
|
||||
@@ -206,7 +273,7 @@ class Database:
|
||||
|
||||
"""
|
||||
table_name = model.TABLE_NAME
|
||||
nonebot.logger.debug(f"Deleting {model} WHERE {condition} {args}")
|
||||
logger.debug(f"数据库 Deleting {model} WHERE {condition} {args}")
|
||||
if not table_name:
|
||||
raise ValueError(f"数据模型{model.__class__.__name__}未提供表名")
|
||||
if model.id is not None:
|
||||
@@ -214,10 +281,12 @@ class Database:
|
||||
if not condition and not allow_empty:
|
||||
raise ValueError("删除操作必须提供条件")
|
||||
self.cursor.execute(f"DELETE FROM {table_name} WHERE {condition}", args)
|
||||
self.conn.commit()
|
||||
if self._is_locked:
|
||||
pass
|
||||
else:
|
||||
self.conn.commit()
|
||||
|
||||
def auto_migrate(self, *args: LiteModel):
|
||||
|
||||
"""
|
||||
自动迁移模型
|
||||
Args:
|
||||
@@ -239,21 +308,35 @@ class Database:
|
||||
new_structure = {}
|
||||
for n_field, n_value in model.dump(by_alias=True).items():
|
||||
if n_field not in ["TABLE_NAME", "id"]:
|
||||
new_structure[self._get_stored_field_prefix(n_value) + n_field] = self._get_stored_type(n_value)
|
||||
new_structure[self._get_stored_field_prefix(n_value) + n_field] = (
|
||||
self._get_stored_type(n_value)
|
||||
)
|
||||
|
||||
# 原有的字段列表
|
||||
existing_structure = dict([(column[1], column[2]) for column in self.cursor.execute(f'PRAGMA table_info({model.TABLE_NAME})').fetchall()])
|
||||
existing_structure = dict(
|
||||
[
|
||||
(column[1], column[2])
|
||||
for column in self.cursor.execute(
|
||||
f"PRAGMA table_info({model.TABLE_NAME})"
|
||||
).fetchall()
|
||||
]
|
||||
)
|
||||
# 检测缺失字段,由于SQLite是动态类型,所以不需要检测类型
|
||||
for n_field, n_type in new_structure.items():
|
||||
if n_field not in existing_structure.keys() and n_field.lower() not in ["id", "table_name"]:
|
||||
default_value = self.DEFAULT_MAPPING.get(n_type, 'NULL')
|
||||
if n_field not in existing_structure.keys() and n_field.lower() not in [
|
||||
"id",
|
||||
"table_name",
|
||||
]:
|
||||
default_value = self.DEFAULT_MAPPING.get(n_type, "NULL")
|
||||
self.cursor.execute(
|
||||
f"ALTER TABLE '{model.TABLE_NAME}' ADD COLUMN {n_field} {n_type} DEFAULT {self.DEFAULT_MAPPING.get(n_type, default_value)}"
|
||||
)
|
||||
|
||||
# 检测多余字段进行删除
|
||||
for e_field in existing_structure.keys():
|
||||
if e_field not in new_structure.keys() and e_field.lower() not in ['id']:
|
||||
if e_field not in new_structure.keys() and e_field.lower() not in [
|
||||
"id"
|
||||
]:
|
||||
self.cursor.execute(
|
||||
f'ALTER TABLE "{model.TABLE_NAME}" DROP COLUMN "{e_field}"'
|
||||
)
|
||||
@@ -270,7 +353,11 @@ class Database:
|
||||
Sqlite3存储字段
|
||||
"""
|
||||
|
||||
if isinstance(value, LiteModel) or isinstance(value, dict) and "TABLE_NAME" in value:
|
||||
if (
|
||||
isinstance(value, LiteModel)
|
||||
or isinstance(value, dict)
|
||||
and "TABLE_NAME" in value
|
||||
):
|
||||
return self.FOREIGN_KEY_PREFIX
|
||||
elif type(value) in self.ITERABLE_TYPE:
|
||||
return self.BYTES_PREFIX
|
||||
@@ -302,8 +389,15 @@ class Database:
|
||||
foreign_value = foreign_value.replace(self.FOREIGN_KEY_PREFIX, "")
|
||||
table_name = foreign_value.split("@")[-1]
|
||||
foreign_id = foreign_value.split("@")[0]
|
||||
fields = [description[1] for description in self.cursor.execute(f"PRAGMA table_info({table_name})").fetchall()]
|
||||
result = self.cursor.execute(f"SELECT * FROM {table_name} WHERE id = ?", (foreign_id,)).fetchone()
|
||||
fields = [
|
||||
description[1]
|
||||
for description in self.cursor.execute(
|
||||
f"PRAGMA table_info({table_name})"
|
||||
).fetchall()
|
||||
]
|
||||
result = self.cursor.execute(
|
||||
f"SELECT * FROM {table_name} WHERE id = ?", (foreign_id,)
|
||||
).fetchone()
|
||||
return dict(zip(fields, result))
|
||||
|
||||
def on_save(self, func: Callable[[LiteModel | Any], None]):
|
||||
@@ -333,29 +427,28 @@ class Database:
|
||||
return wrapper
|
||||
|
||||
TYPE_MAPPING = {
|
||||
int : "INTEGER",
|
||||
float : "REAL",
|
||||
str : "TEXT",
|
||||
bool : "INTEGER",
|
||||
bytes : "BLOB",
|
||||
NoneType : "NULL",
|
||||
# dict : "TEXT",
|
||||
# list : "TEXT",
|
||||
# tuple : "TEXT",
|
||||
# set : "TEXT",
|
||||
|
||||
dict : "BLOB", # LITEYUKIDICT{key_name}
|
||||
list : "BLOB", # LITEYUKILIST{key_name}
|
||||
tuple : "BLOB", # LITEYUKITUPLE{key_name}
|
||||
set : "BLOB", # LITEYUKISET{key_name}
|
||||
LiteModel: "TEXT" # FOREIGN_KEY_{table_name}
|
||||
int: "INTEGER",
|
||||
float: "REAL",
|
||||
str: "TEXT",
|
||||
bool: "INTEGER",
|
||||
bytes: "BLOB",
|
||||
NoneType: "NULL",
|
||||
# dict : "TEXT",
|
||||
# list : "TEXT",
|
||||
# tuple : "TEXT",
|
||||
# set : "TEXT",
|
||||
dict: "BLOB", # LITEYUKIDICT{key_name}
|
||||
list: "BLOB", # LITEYUKILIST{key_name}
|
||||
tuple: "BLOB", # LITEYUKITUPLE{key_name}
|
||||
set: "BLOB", # LITEYUKISET{key_name}
|
||||
LiteModel: "TEXT", # FOREIGN_KEY_{table_name}
|
||||
}
|
||||
DEFAULT_MAPPING = {
|
||||
"TEXT" : "''",
|
||||
"INTEGER": 0,
|
||||
"REAL" : 0.0,
|
||||
"BLOB" : None,
|
||||
"NULL" : None
|
||||
"TEXT": "''",
|
||||
"INTEGER": 0,
|
||||
"REAL": 0.0,
|
||||
"BLOB": None,
|
||||
"NULL": None,
|
||||
}
|
||||
|
||||
# 基础类型
|
||||
@@ -384,21 +477,130 @@ class Database:
|
||||
|
||||
def check_sqlite_keyword(name):
|
||||
sqlite_keywords = [
|
||||
"ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ANALYZE", "AND", "AS", "ASC",
|
||||
"ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN", "BY", "CASCADE", "CASE",
|
||||
"CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT", "CONFLICT", "CONSTRAINT", "CREATE",
|
||||
"CROSS", "CURRENT_DATE", "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT",
|
||||
"DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DROP", "EACH",
|
||||
"ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUSIVE", "EXISTS", "EXPLAIN", "FAIL", "FOR",
|
||||
"FOREIGN", "FROM", "FULL", "GLOB", "GROUP", "HAVING", "IF", "IGNORE", "IMMEDIATE",
|
||||
"IN", "INDEX", "INDEXED", "INITIALLY", "INNER", "INSERT", "INSTEAD", "INTERSECT",
|
||||
"INTO", "IS", "ISNULL", "JOIN", "KEY", "LEFT", "LIKE", "LIMIT", "MATCH", "NATURAL",
|
||||
"NO", "NOT", "NOTNULL", "NULL", "OF", "OFFSET", "ON", "OR", "ORDER", "OUTER", "PLAN",
|
||||
"PRAGMA", "PRIMARY", "QUERY", "RAISE", "RECURSIVE", "REFERENCES", "REGEXP", "REINDEX",
|
||||
"RELEASE", "RENAME", "REPLACE", "RESTRICT", "RIGHT", "ROLLBACK", "ROW", "SAVEPOINT",
|
||||
"SELECT", "SET", "TABLE", "TEMP", "TEMPORARY", "THEN", "TO", "TRANSACTION", "TRIGGER",
|
||||
"UNION", "UNIQUE", "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL", "WHEN",
|
||||
"WHERE", "WITH", "WITHOUT"
|
||||
"ABORT",
|
||||
"ACTION",
|
||||
"ADD",
|
||||
"AFTER",
|
||||
"ALL",
|
||||
"ALTER",
|
||||
"ANALYZE",
|
||||
"AND",
|
||||
"AS",
|
||||
"ASC",
|
||||
"ATTACH",
|
||||
"AUTOINCREMENT",
|
||||
"BEFORE",
|
||||
"BEGIN",
|
||||
"BETWEEN",
|
||||
"BY",
|
||||
"CASCADE",
|
||||
"CASE",
|
||||
"CAST",
|
||||
"CHECK",
|
||||
"COLLATE",
|
||||
"COLUMN",
|
||||
"COMMIT",
|
||||
"CONFLICT",
|
||||
"CONSTRAINT",
|
||||
"CREATE",
|
||||
"CROSS",
|
||||
"CURRENT_DATE",
|
||||
"CURRENT_TIME",
|
||||
"CURRENT_TIMESTAMP",
|
||||
"DATABASE",
|
||||
"DEFAULT",
|
||||
"DEFERRABLE",
|
||||
"DEFERRED",
|
||||
"DELETE",
|
||||
"DESC",
|
||||
"DETACH",
|
||||
"DISTINCT",
|
||||
"DROP",
|
||||
"EACH",
|
||||
"ELSE",
|
||||
"END",
|
||||
"ESCAPE",
|
||||
"EXCEPT",
|
||||
"EXCLUSIVE",
|
||||
"EXISTS",
|
||||
"EXPLAIN",
|
||||
"FAIL",
|
||||
"FOR",
|
||||
"FOREIGN",
|
||||
"FROM",
|
||||
"FULL",
|
||||
"GLOB",
|
||||
"GROUP",
|
||||
"HAVING",
|
||||
"IF",
|
||||
"IGNORE",
|
||||
"IMMEDIATE",
|
||||
"IN",
|
||||
"INDEX",
|
||||
"INDEXED",
|
||||
"INITIALLY",
|
||||
"INNER",
|
||||
"INSERT",
|
||||
"INSTEAD",
|
||||
"INTERSECT",
|
||||
"INTO",
|
||||
"IS",
|
||||
"ISNULL",
|
||||
"JOIN",
|
||||
"KEY",
|
||||
"LEFT",
|
||||
"LIKE",
|
||||
"LIMIT",
|
||||
"MATCH",
|
||||
"NATURAL",
|
||||
"NO",
|
||||
"NOT",
|
||||
"NOTNULL",
|
||||
"NULL",
|
||||
"OF",
|
||||
"OFFSET",
|
||||
"ON",
|
||||
"OR",
|
||||
"ORDER",
|
||||
"OUTER",
|
||||
"PLAN",
|
||||
"PRAGMA",
|
||||
"PRIMARY",
|
||||
"QUERY",
|
||||
"RAISE",
|
||||
"RECURSIVE",
|
||||
"REFERENCES",
|
||||
"REGEXP",
|
||||
"REINDEX",
|
||||
"RELEASE",
|
||||
"RENAME",
|
||||
"REPLACE",
|
||||
"RESTRICT",
|
||||
"RIGHT",
|
||||
"ROLLBACK",
|
||||
"ROW",
|
||||
"SAVEPOINT",
|
||||
"SELECT",
|
||||
"SET",
|
||||
"TABLE",
|
||||
"TEMP",
|
||||
"TEMPORARY",
|
||||
"THEN",
|
||||
"TO",
|
||||
"TRANSACTION",
|
||||
"TRIGGER",
|
||||
"UNION",
|
||||
"UNIQUE",
|
||||
"UPDATE",
|
||||
"USING",
|
||||
"VACUUM",
|
||||
"VALUES",
|
||||
"VIEW",
|
||||
"VIRTUAL",
|
||||
"WHEN",
|
||||
"WHERE",
|
||||
"WITH",
|
||||
"WITHOUT",
|
||||
]
|
||||
return True
|
||||
# if name.upper() in sqlite_keywords:
|
||||
|
||||
@@ -4,17 +4,15 @@ from pydantic import Field
|
||||
|
||||
from .data import Database, LiteModel, Database
|
||||
|
||||
print("导入数据库模块")
|
||||
DATA_PATH = "data/liteyuki"
|
||||
|
||||
user_db: Database = Database(os.path.join(DATA_PATH, "users.ldb"))
|
||||
group_db: Database = Database(os.path.join(DATA_PATH, "groups.ldb"))
|
||||
plugin_db: Database = Database(os.path.join(DATA_PATH, "plugins.ldb"))
|
||||
common_db: Database = Database(os.path.join(DATA_PATH, "common.ldb"))
|
||||
|
||||
# 内存数据库,临时用于存储数据
|
||||
memory_database = {
|
||||
|
||||
}
|
||||
memory_database = {}
|
||||
|
||||
|
||||
class User(LiteModel):
|
||||
@@ -57,6 +55,7 @@ class StoredConfig(LiteModel):
|
||||
|
||||
class TempConfig(LiteModel):
|
||||
"""储存临时键值对的表"""
|
||||
|
||||
TABLE_NAME: str = "temp_data"
|
||||
data: dict = {}
|
||||
|
||||
@@ -68,6 +67,9 @@ def auto_migrate():
|
||||
common_db.auto_migrate(GlobalPlugin(), StoredConfig(), TempConfig())
|
||||
|
||||
|
||||
auto_migrate()
|
||||
|
||||
|
||||
def set_memory_data(key: str, value) -> None:
|
||||
"""
|
||||
设置内存数据库的数据,类似于redis
|
||||
@@ -78,9 +80,7 @@ def set_memory_data(key: str, value) -> None:
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return memory_database.update({
|
||||
key: value
|
||||
})
|
||||
return memory_database.update({key: value})
|
||||
|
||||
|
||||
def get_memory_data(key: str, default=None) -> any:
|
||||
|
||||
+23
-19
@@ -6,10 +6,9 @@ import aiohttp
|
||||
import nonebot
|
||||
import psutil
|
||||
import requests
|
||||
from aiohttp import FormData
|
||||
|
||||
from .. import __VERSION_I__, __VERSION__, __NAME__
|
||||
from .config import load_from_yaml
|
||||
from .. import __NAME__, __VERSION_I__, __VERSION__
|
||||
|
||||
|
||||
class LiteyukiAPI:
|
||||
@@ -32,16 +31,16 @@ class LiteyukiAPI:
|
||||
|
||||
"""
|
||||
return {
|
||||
"name" : __NAME__,
|
||||
"version" : __VERSION__,
|
||||
"version_i" : __VERSION_I__,
|
||||
"python" : f"{platform.python_implementation()} {platform.python_version()}",
|
||||
"os" : f"{platform.system()} {platform.version()} {platform.machine()}",
|
||||
"cpu" : f"{psutil.cpu_count(logical=False)}c{psutil.cpu_count()}t{psutil.cpu_freq().current}MHz",
|
||||
"memory_total": f"{psutil.virtual_memory().total / 1024 ** 3:.2f}吉字节",
|
||||
"memory_used" : f"{psutil.virtual_memory().used / 1024 ** 3:.2f}吉字节",
|
||||
"memory_bot" : f"{psutil.Process(os.getpid()).memory_info().rss / 1024 ** 2:.2f}兆字节",
|
||||
"disk" : f"{psutil.disk_usage('/').total / 1024 ** 3:.2f}吉字节"
|
||||
"name": __NAME__,
|
||||
"version": __VERSION__,
|
||||
"version_i": __VERSION_I__,
|
||||
"python": f"{platform.python_implementation()} {platform.python_version()}",
|
||||
"os": f"{platform.system()} {platform.version()} {platform.machine()}",
|
||||
"cpu": f"{psutil.cpu_count(logical=False)}c{psutil.cpu_count()}t{psutil.cpu_freq().current}MHz",
|
||||
"memory_total": f"{psutil.virtual_memory().total / 1024 ** 3:.2f}吉字节",
|
||||
"memory_used": f"{psutil.virtual_memory().used / 1024 ** 3:.2f}吉字节",
|
||||
"memory_bot": f"{psutil.Process(os.getpid()).memory_info().rss / 1024 ** 2:.2f}兆字节",
|
||||
"disk": f"{psutil.disk_usage('/').total / 1024 ** 3:.2f}吉字节",
|
||||
}
|
||||
|
||||
def bug_report(self, content: str):
|
||||
@@ -57,18 +56,23 @@ class LiteyukiAPI:
|
||||
nonebot.logger.warning(f"正在上报查误:{content}")
|
||||
url = "https://api.liteyuki.icu/bug_report"
|
||||
data = {
|
||||
"liteyuki_id": self.liteyuki_id,
|
||||
"content" : content,
|
||||
"device_info": self.device_info
|
||||
"liteyuki_id": self.liteyuki_id,
|
||||
"content": content,
|
||||
"device_info": self.device_info,
|
||||
}
|
||||
resp = requests.post(url, json=data)
|
||||
if resp.status_code == 200:
|
||||
nonebot.logger.success(f"成功上报差误信息,报文ID为:{resp.json().get('report_id')}")
|
||||
nonebot.logger.success(
|
||||
f"成功上报差误信息,报文ID为:{resp.json().get('report_id')}"
|
||||
)
|
||||
else:
|
||||
nonebot.logger.error(f"差误上报错误:{resp.text}")
|
||||
else:
|
||||
nonebot.logger.warning(f"已禁用自动上报:{content}")
|
||||
|
||||
def register(self):
|
||||
pass
|
||||
|
||||
async def heartbeat_report(self):
|
||||
"""
|
||||
提交心跳,预留接口
|
||||
@@ -77,12 +81,12 @@ class LiteyukiAPI:
|
||||
"""
|
||||
url = "https://api.liteyuki.icu/heartbeat"
|
||||
data = {
|
||||
"liteyuki_id": self.liteyuki_id,
|
||||
"version" : __VERSION__,
|
||||
"liteyuki_id": self.liteyuki_id,
|
||||
"version": __VERSION__,
|
||||
}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(url, json=data) as resp:
|
||||
if resp.status == 200:
|
||||
nonebot.logger.success("心跳成功送达。")
|
||||
else:
|
||||
nonebot.logger.error(f"休克:{await resp.text()}")
|
||||
nonebot.logger.error("休克:{}".format(await resp.text()))
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import threading
|
||||
from multiprocessing import get_context
|
||||
|
||||
import nonebot
|
||||
from nonebot import logger
|
||||
|
||||
reboot_grace_time_limit: int = 20
|
||||
|
||||
_nb_run = nonebot.run
|
||||
|
||||
|
||||
class Reloader:
|
||||
event: threading.Event = None
|
||||
|
||||
@classmethod
|
||||
def reload(cls, delay: int = 0):
|
||||
if cls.event is None:
|
||||
raise RuntimeError()
|
||||
if delay > 0:
|
||||
threading.Timer(delay, function=cls.event.set).start()
|
||||
return
|
||||
cls.event.set()
|
||||
|
||||
|
||||
def _run(ev: threading.Event, *args, **kwargs):
|
||||
Reloader.event = ev
|
||||
_nb_run(*args, **kwargs)
|
||||
|
||||
|
||||
def run(*args, **kwargs):
|
||||
should_exit = False
|
||||
ctx = get_context("spawn")
|
||||
while not should_exit:
|
||||
event = ctx.Event()
|
||||
process = ctx.Process(
|
||||
target=_run,
|
||||
args=(
|
||||
event,
|
||||
*args,
|
||||
),
|
||||
kwargs=kwargs,
|
||||
)
|
||||
process.start()
|
||||
while not should_exit:
|
||||
if event.wait(1):
|
||||
logger.info("Receive reboot event")
|
||||
process.terminate()
|
||||
process.join(reboot_grace_time_limit)
|
||||
if process.is_alive():
|
||||
logger.warning(
|
||||
f"Cannot shutdown gracefully in {reboot_grace_time_limit} second, force kill process."
|
||||
)
|
||||
process.kill()
|
||||
break
|
||||
elif process.is_alive():
|
||||
continue
|
||||
else:
|
||||
should_exit = True
|
||||
|
||||
|
||||
nonebot.run = run
|
||||
@@ -1,6 +0,0 @@
|
||||
from .auto_set_env import auto_set_env
|
||||
|
||||
|
||||
def init(config: dict):
|
||||
auto_set_env(config)
|
||||
return
|
||||
@@ -1,20 +0,0 @@
|
||||
import os
|
||||
|
||||
import dotenv
|
||||
import nonebot
|
||||
|
||||
from .defines import *
|
||||
|
||||
|
||||
def auto_set_env(config: dict):
|
||||
dotenv.load_dotenv(".env")
|
||||
if os.getenv("DRIVER", None) is not None:
|
||||
nonebot.logger.info("Driver 已设入环境变量中,将跳过自动配置环节。")
|
||||
return
|
||||
if config.get("satori", {'enable': False}).get("enable", False):
|
||||
os.environ["DRIVER"] = get_driver_string(ASGI_DRIVER, HTTPX_DRIVER, WEBSOCKETS_DRIVER)
|
||||
nonebot.logger.info("已启用 Satori,将 driver 设为 ASGI+HTTPX+WEBSOCKETS")
|
||||
else:
|
||||
os.environ["DRIVER"] = get_driver_string(ASGI_DRIVER)
|
||||
nonebot.logger.info("已禁用 Satori,将 driver 设为 ASGI")
|
||||
return
|
||||
@@ -1,17 +0,0 @@
|
||||
ASGI_DRIVER = "~fastapi"
|
||||
HTTPX_DRIVER = "~httpx"
|
||||
WEBSOCKETS_DRIVER = "~websockets"
|
||||
|
||||
|
||||
def get_driver_string(*argv):
|
||||
output_string = ""
|
||||
if ASGI_DRIVER in argv:
|
||||
output_string += ASGI_DRIVER
|
||||
for arg in argv:
|
||||
if arg != ASGI_DRIVER:
|
||||
output_string = f"{output_string}+{arg}"
|
||||
return output_string
|
||||
|
||||
|
||||
def get_driver_full_string(*argv):
|
||||
return f"DRIVER={get_driver_string(argv)}"
|
||||
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/8/7 下午10:40
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : __init__.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
from .lib_loader import *
|
||||
|
||||
__all__ = [
|
||||
"load_lib"
|
||||
]
|
||||
@@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/8/7 下午10:40
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : lib_loader.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import platform
|
||||
import ctypes
|
||||
|
||||
LIB_EXT = None
|
||||
PLATFORM = platform.system().lower() # linux, windows, darwin etc
|
||||
ARCH = (
|
||||
platform.machine().lower()
|
||||
) # x86_64/amd64 i386 i686 arm aarch64 ppc64 ppc mips sparc
|
||||
|
||||
if "linux" in PLATFORM:
|
||||
LIB_EXT = "so"
|
||||
elif sys.platform == "darwin":
|
||||
LIB_EXT = "dylib"
|
||||
elif sys.platform == "win32":
|
||||
LIB_EXT = "dll"
|
||||
else:
|
||||
raise RuntimeError("平台 {} 不受支持".format(sys.platform))
|
||||
|
||||
|
||||
def load_lib(lib_name: str) -> ctypes.CDLL:
|
||||
"""
|
||||
Load a dll/so/dylib library, without extension.
|
||||
Args:
|
||||
lib_name: str, path/to/library without extension
|
||||
Returns:
|
||||
xxx_{platform}_{arch}.{ext} ctypes.CDLL
|
||||
"""
|
||||
whole_path = f"{lib_name}_{PLATFORM}_{ARCH}.{LIB_EXT}"
|
||||
if not os.path.exists(whole_path):
|
||||
raise FileNotFoundError(f"未见动态链接库 {whole_path}")
|
||||
return ctypes.CDLL(whole_path)
|
||||
Reference in New Issue
Block a user