Ⓜ️手动从旧梦 81a191f merge

This commit is contained in:
2024-08-12 11:44:30 +08:00
parent 068feaa591
commit 2d67a703bd
214 changed files with 6457 additions and 10418 deletions
+13 -8
View File
@@ -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__}) 正在运行")
-14
View File
@@ -1,14 +0,0 @@
from . import (
satori,
onebot
)
def init(config: dict):
onebot.init()
satori.init(config)
def register():
onebot.register()
satori.register()
-12
View File
@@ -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)
-26
View File
@@ -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)
+26
View File
@@ -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)
-10
View File
@@ -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
View File
@@ -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字段可能为Nonefix 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:
+7 -7
View File
@@ -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
View File
@@ -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()))
-61
View File
@@ -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
-6
View File
@@ -1,6 +0,0 @@
from .auto_set_env import auto_set_env
def init(config: dict):
auto_set_env(config)
return
-20
View File
@@ -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
-17
View File
@@ -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)}"
+15
View File
@@ -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"
]
+43
View File
@@ -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)