summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ATRI/plugins/console/__init__.py82
-rw-r--r--ATRI/plugins/console/data_source.py96
-rw-r--r--ATRI/plugins/console/driver/__init__.py48
-rw-r--r--ATRI/plugins/console/driver/api.py262
-rw-r--r--ATRI/plugins/console/driver/data_source.py167
-rw-r--r--ATRI/plugins/console/driver/depends.py38
-rw-r--r--ATRI/plugins/console/driver/models.py38
-rw-r--r--ATRI/plugins/console/driver/path.py14
-rw-r--r--ATRI/plugins/console/driver/view.py101
-rw-r--r--ATRI/plugins/console/listener.py2
-rw-r--r--ATRI/plugins/console/models.py27
11 files changed, 431 insertions, 444 deletions
diff --git a/ATRI/plugins/console/__init__.py b/ATRI/plugins/console/__init__.py
index b09cb29..3711f86 100644
--- a/ATRI/plugins/console/__init__.py
+++ b/ATRI/plugins/console/__init__.py
@@ -1,5 +1,4 @@
-import json
-from datetime import datetime, timedelta
+from datetime import datetime
from nonebot.params import ArgPlainText
from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent
@@ -9,14 +8,9 @@ from ATRI.log import log
from ATRI.service import Service
from ATRI.permission import MASTER
from ATRI.message import MessageBuilder
-from ATRI.exceptions import WriteFileError
from ATRI.utils.apscheduler import scheduler
-from .data_source import Console, CONSOLE_DIR
-from .models import AuthData
-
-
-__AUTH_FILE_PATH = CONSOLE_DIR / "data.json"
+from .data_source import AuthDealer, get_host_ip
plugin = (
@@ -28,9 +22,8 @@ plugin = (
)
-def __del_auth_key():
- with open(__AUTH_FILE_PATH, "w", encoding="utf-8") as w:
- w.write(json.dumps({}))
+async def __del_auth_key():
+ await AuthDealer.clear()
log.warning("控制台验证密钥已过期")
@@ -48,54 +41,40 @@ async def _(event: PrivateMessageEvent, is_pub_n: str = ArgPlainText("is_pub_n")
.text("Tip: 该内容请尽可能地复杂, 请勿使用中文")
)
- if not __AUTH_FILE_PATH.is_file():
- with open(__AUTH_FILE_PATH, "w", encoding="utf-8") as w:
- w.write(json.dumps(dict()))
- else:
- raw_data = json.loads(__AUTH_FILE_PATH.read_bytes())
-
- data = raw_data.get("data")
- if data:
- now_time = datetime.now().timestamp()
- if now_time < data["dead_time"] and data.get("dead_time"):
- raw_last_time = data["dead_time"] - now_time
- last_time = datetime.fromtimestamp(raw_last_time).minute
- await gen_console_key.finish(
- MessageBuilder("之前生成的密钥还在有效时间内奥")
- .text(f"Token: {data['token']}")
- .text(f"剩余有效时间: {last_time} min")
- )
- else:
- with open(__AUTH_FILE_PATH, "w", encoding="utf-8") as w:
- w.write(json.dumps(dict()))
+ auth_info = AuthDealer.get()
+ if auth_info:
+ now_time = datetime.now().timestamp()
+ if now_time < auth_info.dead_time:
+ raw_last_time = auth_info.dead_time - now_time
+ last_time = datetime.fromtimestamp(raw_last_time).minute
+ await gen_console_key.finish(
+ MessageBuilder("之前生成的密钥还在有效时间内奥")
+ .text(f"Token: {auth_info.token}")
+ .text(f"剩余有效时间: {last_time} min")
+ )
+ await AuthDealer.clear()
if is_pub_n != "y":
- host = str(await Console().get_host_ip(False))
+ host = str(await get_host_ip(False))
await gen_console_key.send("没有公网吗...嗯知道了")
else:
- host = str(await Console().get_host_ip(True))
-
+ host = str(await get_host_ip(True))
port = conf.BotConfig.port
- token = Console().get_random_str(20)
- dead_time = datetime.now() + timedelta(minutes=15)
-
- data = json.loads(__AUTH_FILE_PATH.read_bytes())
- data["data"] = AuthData(token=token, dead_time=dead_time.timestamp()).dict()
- with open(__AUTH_FILE_PATH, "w", encoding="utf-8") as w:
- w.write(json.dumps(data))
+ auth = AuthDealer()
+ data = await auth.store()
msg = (
MessageBuilder("控制台信息已生成!")
.text(f"请访问: {host}:{port}")
- .text(f"Token: {token}")
+ .text(f"Token: {auth.get_token()}")
.text("该 token 有效时间为 15min")
)
scheduler.add_job(
__del_auth_key,
name="清除后台验证凭证",
- next_run_time=dead_time, # type: ignore
- misfire_grace_time=15, # type: ignore
+ next_run_time=datetime.fromtimestamp(data.dead_time),
+ misfire_grace_time=15,
)
await gen_console_key.finish(msg)
@@ -114,20 +93,11 @@ async def _(is_sure: str = ArgPlainText("is_sure_d")):
if is_sure != "y":
await del_console_key.finish("反悔了呢...")
- df = CONSOLE_DIR / "data.json"
- if not df.is_file():
+ data = AuthDealer.get()
+ if data is None:
await del_console_key.finish("你还没向咱索取凭证呢...私戳咱键入 /con.auth 以获取")
- try:
- data = json.loads(df.read_bytes())
-
- del data["data"]
-
- with open(df, "w", encoding="utf-8") as w:
- w.write(json.dumps(data))
- except Exception:
- await del_console_key.send("销毁失败了...请至此处自行删除文件:\n" + str(df))
- raise WriteFileError("Writing / Reading file: " + str(df) + " failed!")
+ await AuthDealer.clear()
await del_console_key.finish("销毁成功!如需再次获取: /con.auth")
diff --git a/ATRI/plugins/console/data_source.py b/ATRI/plugins/console/data_source.py
index b4a28cb..ab6b51b 100644
--- a/ATRI/plugins/console/data_source.py
+++ b/ATRI/plugins/console/data_source.py
@@ -2,59 +2,75 @@ import json
import socket
import string
import zipfile
-from random import sample
+import hashlib
from pathlib import Path
+from random import sample
+from typing import Optional
+from datetime import datetime, timedelta
-from ATRI.utils import request
-from ATRI.exceptions import WriteFileError
+from ATRI.utils import request, FileDealer
from ATRI.log import log
+from .models import AuthData
+
CONSOLE_DIR = Path(".") / "data" / "plugins" / "console"
CONSOLE_DIR.mkdir(parents=True, exist_ok=True)
-class Console:
- @staticmethod
- async def get_host_ip(is_pub: bool):
- if is_pub:
- data = await request.get("https://ifconfig.me/ip")
- return data.text
-
- s = None
- try:
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- s.connect(("8.8.8.8", 80))
- ip = s.getsockname()[0]
- return ip
- finally:
- if s:
- s.close()
-
- @staticmethod
- def get_random_str(k: int) -> str:
- return "".join(sample(string.ascii_letters + string.digits, k))
-
- @staticmethod
- def get_auth_info() -> dict:
- df = CONSOLE_DIR / "data.json"
- if not df.is_file():
- try:
- with open(df, "w", encoding="utf-8") as w:
- w.write(json.dumps(dict()))
- except Exception:
- raise WriteFileError("Writing file: " + str(df) + " failed!")
-
- base_data: dict = json.loads(df.read_bytes())
- data = base_data.get("data", None)
- if not data:
- return {"data": None}
+class AuthDealer:
+ AUTH_FILE_PATH = CONSOLE_DIR / "data.json"
+
+ def __init__(self):
+ self.token = str().join(sample(string.ascii_letters + string.digits, 20))
+
+ def get_token(self):
+ return self.token
+
+ def get_md5(self):
+ hl = hashlib.md5()
+ hl.update(self.token.encode(encoding="utf-8"))
+ return hl.hexdigest()
+
+ async def store(self) -> AuthData:
+ dead_time = (datetime.now() + timedelta(minutes=15)).timestamp()
+ data = AuthData(token=self.token, md5=self.get_md5(), dead_time=int(dead_time))
+ await FileDealer(self.AUTH_FILE_PATH).write(json.dumps(data.dict()))
return data
+ @classmethod
+ def get(cls) -> Optional[AuthData]:
+ return (
+ AuthData.parse_file(cls.AUTH_FILE_PATH)
+ if cls.AUTH_FILE_PATH.is_file()
+ and json.loads(cls.AUTH_FILE_PATH.read_bytes())
+ else None
+ )
+
+ @classmethod
+ async def clear(cls):
+ await FileDealer(cls.AUTH_FILE_PATH).write(json.dumps(dict()))
+
+
+async def get_host_ip(is_pub: bool):
+ if is_pub:
+ data = await request.get("https://ifconfig.me/ip")
+ return data.text
+
+ s = None
+ try:
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.connect(("8.8.8.8", 80))
+ ip = s.getsockname()[0]
+ return ip
+ finally:
+ if s:
+ s.close()
+
FRONTEND_DIR = CONSOLE_DIR / "frontend"
FRONTEND_DIR.mkdir(parents=True, exist_ok=True)
-CONSOLE_RESOURCE_URL = (
+__CONSOLE_RESOURCE_URL = (
"https://jsd.imki.moe/gh/kyomotoi/Project-ATRI-Console@main/archive/dist.zip"
)
@@ -63,7 +79,7 @@ async def init_resource():
log.info("控制台初始化中...")
try:
- resp = await request.get(CONSOLE_RESOURCE_URL)
+ resp = await request.get(__CONSOLE_RESOURCE_URL)
except Exception:
log.error("控制台资源装载失败, 将无法访问管理界面")
return
diff --git a/ATRI/plugins/console/driver/__init__.py b/ATRI/plugins/console/driver/__init__.py
index e5dfdc6..b6b9f89 100644
--- a/ATRI/plugins/console/driver/__init__.py
+++ b/ATRI/plugins/console/driver/__init__.py
@@ -5,32 +5,14 @@ from fastapi.middleware.cors import CORSMiddleware
from ATRI import conf
from ATRI.log import log
-from ATRI.plugins.console.data_source import FRONTEND_DIR
-from .view import (
- handle_auther,
- handle_base_uri,
- handle_control_service,
- handle_edit_block,
- handle_get_block_list,
- handle_get_service_list,
- handle_runtime_info,
- handle_message_deal_info,
-)
+from .path import *
+from .api import *
-CONSOLE_API_URI = "/capi" # base point
-CONSOLE_API_AUTH_URI = "/capi/auth" # 验证后台许可
-CONSOLE_API_RUNTIME_URI = "/capi/runtime" # 获取运行占用信息
-CONSOLE_API_MESSAGE_URI = "/capi/message" # 获取信息处理信息
+from ..data_source import FRONTEND_DIR
-CONSOLE_API_SERVICE_LIST_URI = "/capi/service/list" # 获取服务列表
-CONSOLE_API_SERVICE_CONTROL_URI = "/capi/service/control" # 对服务作出修改
-CONSOLE_API_BLOCK_LIST_URI = "/capi/block/list" # 获取封禁列表
-CONSOLE_API_BLOCK_EDIT_URI = "/capi/block/edit" # 编辑封禁列表
-
-
-def register_routes(driver: Driver):
+def register_driver(driver: Driver):
app = driver.server_app
origins = ["*"]
@@ -42,16 +24,18 @@ def register_routes(driver: Driver):
allow_headers=["Content-Type"],
)
- app.get(CONSOLE_API_URI)(handle_base_uri)
- app.get(CONSOLE_API_AUTH_URI)(handle_auther)
- app.get(CONSOLE_API_RUNTIME_URI)(handle_runtime_info)
- app.get(CONSOLE_API_MESSAGE_URI)(handle_message_deal_info)
+ app.get(CONSOLE_BASE_URL)(base_url)
+
+ app.get(CONSOLE_AUTH_URL)(auth_info)
+
+ app.websocket(CONSOLE_RUNTIME_INFO_URL)(runtime_info)
+ app.websocket(CONSOLE_MESSAGE_INFO_URL)(message_info)
- app.get(CONSOLE_API_SERVICE_LIST_URI)(handle_get_service_list)
- app.get(CONSOLE_API_SERVICE_CONTROL_URI)(handle_control_service)
+ app.get(CONSOLE_SERVICE_LIST_URL)(service_list)
+ app.get(CONSOLE_SERVICE_EDIT_URL)(edit_service)
- app.get(CONSOLE_API_BLOCK_LIST_URI)(handle_get_block_list)
- app.get(CONSOLE_API_BLOCK_EDIT_URI)(handle_edit_block)
+ app.get(CONSOLE_BLOCK_LIST_URL)(block_list_info)
+ app.get(CONSOLE_BLOCK_EDIT_URL)(edit_block_list)
static_path = str(FRONTEND_DIR)
app.mount(
@@ -64,6 +48,6 @@ def register_routes(driver: Driver):
def init_driver():
from ATRI import driver
+ register_driver(driver()) # type: ignore
c_url = f"{conf.BotConfig.host}:{conf.BotConfig.port}"
- log.info(f"控制台将运行于: http://{c_url} 对应API节点为: /capi")
- register_routes(driver()) # type: ignore
+ log.success(f"控制台将运行于: http://{c_url} 对应API节点为: /capi")
diff --git a/ATRI/plugins/console/driver/api.py b/ATRI/plugins/console/driver/api.py
index 4750148..4b78906 100644
--- a/ATRI/plugins/console/driver/api.py
+++ b/ATRI/plugins/console/driver/api.py
@@ -1,202 +1,86 @@
-import os
-import json
-import time
-import psutil
-from pathlib import Path
-from datetime import datetime
+import asyncio
-from ATRI.service import ServiceTools, SERVICES_DIR
-from ATRI.exceptions import GetStatusError
-from ..models import PlatformRuntimeInfo, BotRuntimeInfo, ServiceInfo
+from fastapi import Depends, status
+from starlette.websockets import WebSocket, WebSocketState
+from websockets.exceptions import ConnectionClosedOK
-
-def get_processing_data() -> tuple:
- try:
- p_cpu = psutil.cpu_percent(interval=1)
- p_mem = psutil.virtual_memory().percent
- disk = psutil.disk_usage("/").percent
- inte_send = psutil.net_io_counters().bytes_sent / 1000000 # type: ignore
- inte_recv = psutil.net_io_counters().bytes_recv / 1000000 # type: ignore
-
- process = psutil.Process(os.getpid())
- b_cpu = process.cpu_percent(interval=1)
- b_mem = process.memory_percent(memtype="rss")
-
- now = time.time()
- boot = psutil.boot_time()
- b = process.create_time()
- boot_time = str(
- datetime.utcfromtimestamp(now).replace(microsecond=0)
- - datetime.utcfromtimestamp(boot).replace(microsecond=0)
- )
- bot_time = str(
- datetime.utcfromtimestamp(now).replace(microsecond=0)
- - datetime.utcfromtimestamp(b).replace(microsecond=0)
- )
- except Exception:
- raise GetStatusError("Getting runtime failed.")
-
- if p_cpu > 90: # type: ignore
- msg = "咱感觉有些头晕..."
- if p_mem > 90:
- msg = "咱感觉有点头晕并且有点累..."
- elif p_mem > 90:
- msg = "咱感觉有点累..."
- elif disk > 90:
- msg = "咱感觉身体要被塞满了..."
- else:
- msg = "アトリは、高性能ですから!"
-
- return (
- PlatformRuntimeInfo(
- stat_msg=msg,
- cpu_percent=str(p_cpu),
- mem_percent=str(p_mem),
- disk_percent=str(disk),
- inte_send=str(inte_send),
- inte_recv=str(inte_recv),
- boot_time=boot_time,
- ).dict(),
- BotRuntimeInfo(
- cpu_percent=str(b_cpu), mem_percent=str(b_mem), bot_run_time=bot_time
- ).dict(),
- )
-
-
-def get_service_list() -> dict:
- result = dict()
-
- files = os.listdir(SERVICES_DIR)
- for f in files:
- # Thank you, MacOS
- if f == ".DS_Store":
- continue
-
- serv_path = SERVICES_DIR / f
- data = json.loads(serv_path.read_bytes())
-
- serv_name = data["service"]
- serv_docs = data["docs"]
- serv_is_enabled = data["enabled"]
- serv_disable_user = data["disable_user"]
- serv_disable_group = data["disable_group"]
-
- result[serv_name] = ServiceInfo(
- service_name=serv_name,
- service_docs=serv_docs,
- is_enabled=serv_is_enabled,
- disable_user=serv_disable_user,
- disable_group=serv_disable_group,
- ).dict()
-
- return result
-
-
-def control_service(
- serv_name: str, is_enab: int, enab_u: str, enab_g: str, disab_u: str, disab_g: str
-) -> tuple:
- try:
- serv_data = ServiceTools().load_service(serv_name)
- except Exception:
- return False, dict()
-
- if is_enab != 1:
- if is_enab == 0:
- serv_data["enabled"] = False
- else:
- serv_data["enabled"] = True
-
- if enab_u:
- if enab_u not in serv_data["disable_user"]:
- return False, {"msg": "Target not in list"}
- serv_data["disable_user"].remove(enab_u)
-
- if enab_g:
- if enab_g not in serv_data["disable_user"]:
- return False, {"msg": "Target not in list"}
- serv_data["disable_group"].remove(enab_g)
-
- if disab_u:
- if disab_u in serv_data["disable_user"]:
- return False, {"msg": "Target already exists in list"}
- serv_data["disable_user"].append(disab_u)
-
- if disab_g:
- if disab_g in serv_data["disable_user"]:
- return False, {"msg": "Target already exists in list"}
- serv_data["disable_group"].append(disab_g)
-
- try:
- ServiceTools().save_service(serv_data, serv_name)
- except Exception:
- return False, dict()
-
- return True, serv_data
-
-
-MANEGE_DIR = Path(".") / "data" / "plugins" / "manege"
+from .depends import *
+from .data_source import (
+ get_process_info,
+ get_service_list,
+ edit_service as _edit_service,
+ get_block_list,
+ edit_block_list as _edit_block_list,
+)
+from ..listener import get_message_info
-def get_block_list() -> dict:
- u_data = dict()
- g_data = dict()
+def base_url(_=Depends(http_author)):
+ return {"status": status.HTTP_204_NO_CONTENT, "msg": "该路径仅供控制台加载"}
- u_f = "block_user.json"
- path = MANEGE_DIR / u_f
- if path.is_file():
- u_data = json.loads(path.read_bytes())
- g_f = "block_group.json"
- path = MANEGE_DIR / g_f
- if path.is_file():
- g_data = json.loads(path.read_bytes())
+def auth_info(_=Depends(http_author)):
+ return {"status": status.HTTP_200_OK, "detail": "OK"}
- return {"user": u_data, "group": g_data}
-
-def edit_block_list(is_enab: bool, user_id: str, group_id: str) -> tuple:
- d = get_block_list()
- u_d = d["user"]
- g_d = d["group"]
-
- now_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
-
- if is_enab:
- if user_id:
- if user_id in u_d:
- return False, {"msg": "Target already exists in list"}
- u_d[user_id] = now_time
-
- if group_id:
- if group_id in g_d:
- return False, {"msg": "Target already exists in list"}
- g_d[group_id] = now_time
- else:
- if user_id:
- if user_id not in u_d:
- return False, {"msg": "Target not in list"}
- del u_d[user_id]
-
- if group_id:
- if group_id not in g_d:
- return False, {"msg": "Target not in list"}
- del g_d[group_id]
+async def runtime_info(websocket: WebSocket, _pass=Depends(websocket_author)):
+ if not _pass:
+ return
+ await websocket.accept()
try:
- u_f = "block_user.json"
- path = MANEGE_DIR / u_f
- with open(path, "w", encoding="utf-8") as w:
- w.write(json.dumps(u_d))
-
- g_f = "block_group.json"
- path = MANEGE_DIR / g_f
- with open(path, "w", encoding="utf-8") as w:
- w.write(json.dumps(g_d))
- except Exception:
- return False, dict()
+ while websocket.client_state == WebSocketState.CONNECTED:
+ await websocket.send_json(get_process_info())
+ await asyncio.sleep(1)
+ except ConnectionClosedOK:
+ pass
+ finally:
+ await websocket.close()
+ return
- return True, {"user": u_d, "group": g_d}
+async def message_info(websocket: WebSocket, _pass=Depends(websocket_author)):
+ if not _pass:
+ return
+ await websocket.accept()
-def get_group_list():
- ...
+ try:
+ while websocket.client_state == WebSocketState.CONNECTED:
+ await websocket.send_json(get_message_info())
+ await asyncio.sleep(1)
+ except ConnectionClosedOK:
+ pass
+ finally:
+ await websocket.close()
+ return
+
+
+def service_list(_=Depends(http_author)):
+ return {"status": status.HTTP_200_OK, "data": get_service_list()}
+
+
+def edit_service(
+ service: str,
+ global_enabled: str = "2",
+ enabled: str = "2",
+ user: str = str(),
+ group: str = str(),
+ _=Depends(http_author),
+):
+ return {
+ "status": status.HTTP_200_OK,
+ "data": _edit_service(
+ service, int(global_enabled), bool(int(enabled)), user, group
+ ),
+ }
+
+
+def block_list_info(_=Depends(http_author)):
+ return {"status": status.HTTP_200_OK, "data": get_block_list()}
+
+
+def edit_block_list(enabled: str, user_id: str = str(), group_id: str = str(), _=Depends(http_author)):
+ return {
+ "status": status.HTTP_200_OK,
+ "data": _edit_block_list(bool(int(enabled)), user_id, group_id),
+ }
diff --git a/ATRI/plugins/console/driver/data_source.py b/ATRI/plugins/console/driver/data_source.py
new file mode 100644
index 0000000..12fb938
--- /dev/null
+++ b/ATRI/plugins/console/driver/data_source.py
@@ -0,0 +1,167 @@
+import os
+import json
+import psutil
+from pathlib import Path
+from datetime import datetime
+
+from ATRI.exceptions import GetStatusError
+
+from ATRI.utils import FileDealer
+from ATRI.service import ServiceTools, SERVICES_DIR
+
+from . import models
+
+
+def get_process_info() -> dict:
+ try:
+ platform_cpu = psutil.cpu_percent(interval=1)
+ platform_mem = psutil.virtual_memory().percent
+ platform_disk = psutil.disk_usage("/").percent
+ platform_net_sent = str(psutil.net_io_counters().bytes_sent / 1000000)
+ platform_net_recv = str(psutil.net_io_counters().bytes_recv / 1000000)
+
+ process = psutil.Process(os.getpid())
+ bot_cpu = str(process.cpu_percent(interval=1))
+ bot_mem = str(process.memory_percent(memtype="rss"))
+
+ now_time = datetime.now().timestamp()
+ _boot_time = psutil.boot_time()
+ _bot_run_time = process.create_time()
+ boot_time = str(
+ datetime.utcfromtimestamp(now_time).replace(microsecond=0)
+ - datetime.utcfromtimestamp(_boot_time).replace(microsecond=0)
+ )
+ bot_run_time = str(
+ datetime.utcfromtimestamp(now_time).replace(microsecond=0)
+ - datetime.utcfromtimestamp(_bot_run_time).replace(microsecond=0)
+ )
+ except Exception:
+ raise GetStatusError("获取实例运行信息失败")
+
+ stat_msg = "アトリは、高性能ですから!"
+ if platform_cpu > 90:
+ stat_msg = "咱感觉有些头晕..."
+ if platform_mem > 90:
+ stat_msg = "咱感觉有点头晕并且有点累..."
+ elif platform_mem > 90:
+ stat_msg = "咱感觉有点累..."
+ elif platform_disk > 90:
+ stat_msg = "咱感觉身体要被塞满了..."
+
+ platform_cpu = str(platform_cpu)
+ platform_mem = str(platform_mem)
+ platform_disk = str(platform_disk)
+
+ return models.RuntimeInfo(
+ status_message=stat_msg,
+ platform_info=models.PlatformRuntimeInfo(
+ cpu_percent=platform_cpu,
+ mem_percent=platform_mem,
+ disk_percent=platform_disk,
+ net_sent=platform_net_sent,
+ net_recv=platform_net_recv,
+ boot_time=boot_time,
+ ),
+ bot_info=models.BotRuntimeInfo(
+ cpu_percent=bot_cpu, mem_percent=bot_mem, run_time=bot_run_time
+ ),
+ ).dict()
+
+
+def get_service_list() -> dict:
+ result = dict()
+
+ files = os.listdir(SERVICES_DIR)
+ for file in files:
+ # Thank you, MacOS
+ if file == ".DS_Store":
+ continue
+
+ service_path = SERVICES_DIR / file
+ data = models.ServiceInfo.parse_file(service_path)
+ result[data.service] = data.dict()
+
+ return result
+
+
+def edit_service(
+ service: str, global_enabled: int, enabled: bool, user_id: str, group_id: str
+):
+ data = ServiceTools.load_service(service)
+
+ if global_enabled != 2 and global_enabled:
+ data.enabled = bool(global_enabled)
+ else:
+ data.enabled = False
+ if user_id or group_id:
+ if enabled:
+ if user_id not in data.disable_user:
+ return {"detail": "用户不存在于禁用名单"}
+ else:
+ data.disable_user.remove(user_id)
+
+ if group_id not in data.disable_group:
+ return {"detail": "群不存在于禁用名单"}
+ else:
+ data.disable_group.remove(group_id)
+ else:
+ if user_id not in data.disable_user:
+ data.disable_user.append(user_id)
+ else:
+ return {"detail": "用户已存在于禁用名单"}
+
+ if group_id not in data.disable_group:
+ data.disable_group.append(group_id)
+ else:
+ return {"detail": "群已存在于禁用名单"}
+
+ ServiceTools.save_service(data.dict(), service)
+
+ return {"detail": "操作完成~"}
+
+
+def get_block_list() -> models.BlockInfo:
+ file_dir = Path(".") / "data" / "plugins" / "manege"
+ path = file_dir / "block_user.json"
+ user_data = json.loads(path.read_bytes())
+
+ path = file_dir / "block_group.json"
+ group_data = json.loads(path.read_bytes())
+
+ return models.BlockInfo(user=user_data, group=group_data)
+
+
+async def edit_block_list(enabled: bool, user_id: str, group_id: str):
+ data = get_block_list()
+ now_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ if enabled:
+ if user_id:
+ if user_id in data.user:
+ return {"detail": "用户已存在于黑名单"}
+ else:
+ data.user[user_id] = now_time
+ if group_id:
+ if group_id in data.group:
+ return {"detail": "群已存在于黑名单"}
+ else:
+ data.group[group_id] = now_time
+ else:
+ if user_id:
+ if user_id in data.user:
+ del data.user[user_id]
+ else:
+ return {"detail": "用户不存在于黑名单"}
+ if group_id:
+ if group_id in data.group:
+ del data.group[group_id]
+ else:
+ return {"detail": "群不存在于黑名单"}
+
+ file_dir = Path(".") / "data" / "plugins" / "manege"
+ path = file_dir / "block_user.json"
+ await FileDealer(path).write(json.dumps(data.user))
+
+ path = file_dir / "block_group.json"
+ await FileDealer(path).write(json.dumps(data.group))
+
+ return {"detail": "操作完成~"}
diff --git a/ATRI/plugins/console/driver/depends.py b/ATRI/plugins/console/driver/depends.py
new file mode 100644
index 0000000..8205f44
--- /dev/null
+++ b/ATRI/plugins/console/driver/depends.py
@@ -0,0 +1,38 @@
+from typing import Union
+from datetime import datetime
+
+from fastapi import Query, HTTPException, status
+from starlette.websockets import WebSocket
+
+from ..data_source import AuthDealer
+
+
+def http_author(token: Union[str, None] = Query(default=None)):
+ data = AuthDealer.get()
+ if data is None:
+ raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="验证信息不存在")
+
+ now_time = datetime.now().timestamp()
+ if token != data.token:
+ raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="密钥不匹配, 请检查")
+ elif now_time > data.dead_time:
+ raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="密钥已过期")
+ else:
+ return token
+
+
+async def websocket_author(
+ websocket: WebSocket, token: Union[str, None] = Query(default=None)
+):
+ data = AuthDealer.get()
+ if not data:
+ await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
+ return
+
+ now_time = datetime.now().timestamp()
+ if token != data.token:
+ await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
+ elif now_time > data.dead_time:
+ await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
+ else:
+ return token
diff --git a/ATRI/plugins/console/driver/models.py b/ATRI/plugins/console/driver/models.py
new file mode 100644
index 0000000..cbc110d
--- /dev/null
+++ b/ATRI/plugins/console/driver/models.py
@@ -0,0 +1,38 @@
+from pydantic import BaseModel
+
+
+class PlatformRuntimeInfo(BaseModel):
+ cpu_percent: str
+ mem_percent: str
+ disk_percent: str
+ net_sent: str
+ net_recv: str
+ boot_time: str
+
+
+class BotRuntimeInfo(BaseModel):
+ cpu_percent: str
+ mem_percent: str
+ run_time: str
+
+
+class RuntimeInfo(BaseModel):
+ status_message: str
+ platform_info: PlatformRuntimeInfo
+ bot_info: BotRuntimeInfo
+
+
+class ServiceInfo(BaseModel):
+ service: str
+ docs: str
+ permission: list
+ cmd_list: dict
+ enabled: bool
+ only_admin: bool
+ disable_user: list
+ disable_group: list
+
+
+class BlockInfo(BaseModel):
+ user: dict
+ group: dict
diff --git a/ATRI/plugins/console/driver/path.py b/ATRI/plugins/console/driver/path.py
new file mode 100644
index 0000000..7009eb7
--- /dev/null
+++ b/ATRI/plugins/console/driver/path.py
@@ -0,0 +1,14 @@
+CONSOLE_BASE_URL = "/capi"
+
+CONSOLE_AUTH_URL = CONSOLE_BASE_URL + "/auth"
+
+CONSOLE_RUNTIME_INFO_URL = CONSOLE_BASE_URL + "/runtime"
+CONSOLE_MESSAGE_INFO_URL = CONSOLE_BASE_URL + "/message"
+
+CONSOLE_SERVICE_BASE_URL = CONSOLE_BASE_URL + "/service"
+CONSOLE_SERVICE_LIST_URL = CONSOLE_SERVICE_BASE_URL + "/list"
+CONSOLE_SERVICE_EDIT_URL = CONSOLE_SERVICE_BASE_URL + "/edit"
+
+CONSOLE_BLOCK_BASE_URL = CONSOLE_BASE_URL + "/block"
+CONSOLE_BLOCK_LIST_URL = CONSOLE_BLOCK_BASE_URL + "/list"
+CONSOLE_BLOCK_EDIT_URL = CONSOLE_BLOCK_BASE_URL + "/edit"
diff --git a/ATRI/plugins/console/driver/view.py b/ATRI/plugins/console/driver/view.py
deleted file mode 100644
index 624e41f..0000000
--- a/ATRI/plugins/console/driver/view.py
+++ /dev/null
@@ -1,101 +0,0 @@
-from ..data_source import Console
-from ..listener import get_message_deal_info
-from .api import (
- control_service,
- edit_block_list,
- get_block_list,
- get_processing_data,
- get_service_list,
-)
-
-
-def auth_token(token: str) -> tuple:
- auth_data: dict = Console().get_auth_info()
- if not auth_data.get("token", None):
- return False, {"status": 500, "msg": "This bot is not create auth data yet."}
- _token = auth_data["token"]
- if token != _token:
- return False, {"status": 403, "msg": "Token error, please check again."}
- else:
- return True, {"status": 200, "msg": "OK"}
-
-
-def handle_base_uri():
- return {"status": 204, "msg": "This path just for console load."}
-
-
-def handle_auther(token: str):
- auth, data = auth_token(token)
- return data if auth else data
-
-
-def handle_runtime_info(token: str):
- auth, data = auth_token(token)
- if not auth:
- return data
-
- plat, bot = get_processing_data()
- return {"status": 200, "data": {"platform": plat, "bot": bot}}
-
-
-def handle_message_deal_info(token: str):
- auth, data = auth_token(token)
- if not auth:
- return data
-
- return {"status": 200, "data": get_message_deal_info()}
-
-
-def handle_get_service_list(token: str):
- auth, data = auth_token(token)
- if not auth:
- return data
-
- return {"status": 200, "data": get_service_list()}
-
-
-def handle_control_service(
- token: str,
- service: str,
- is_enabled: int = 1,
- enabled_user: str = str(),
- enabled_group: str = str(),
- disable_user: str = str(),
- disable_group: str = str(),
-):
- auth, data = auth_token(token)
- if not auth:
- return data
-
- is_ok, data = control_service(
- service, is_enabled, enabled_user, enabled_group, disable_user, disable_group
- )
- if not is_ok:
- return {"status": 422, "msg": "Dealing service data failed"}
-
- return {"status": 200, "data": data}
-
-
-def handle_get_block_list(token: str):
- auth, data = auth_token(token)
- if not auth:
- return data
-
- return {"status": 200, "data": get_block_list()}
-
-
-def handle_edit_block(
- token: str,
- is_enabled: bool,
- user_id: str = str(),
- group_id: str = str(),
-):
- auth, data = auth_token(token)
- if not auth:
- return data
-
- is_ok, data = edit_block_list(is_enabled, user_id, group_id)
- if not is_ok:
- return {"status": 422, "msg": "Dealing block data failed"}
-
- return {"status": 200, "data": data}
diff --git a/ATRI/plugins/console/listener.py b/ATRI/plugins/console/listener.py
index 0ae5c25..2c12c75 100644
--- a/ATRI/plugins/console/listener.py
+++ b/ATRI/plugins/console/listener.py
@@ -30,7 +30,7 @@ async def _(exception: Optional[Exception]):
total_r_m += 1
-def get_message_deal_info() -> dict:
+def get_message_info() -> dict:
return MessageDealerInfo(
recv_msg=str(recv_msg),
deal_msg=str(deal_msg),
diff --git a/ATRI/plugins/console/models.py b/ATRI/plugins/console/models.py
index 92371f8..f7ddf8d 100644
--- a/ATRI/plugins/console/models.py
+++ b/ATRI/plugins/console/models.py
@@ -3,23 +3,8 @@ from pydantic import BaseModel
class AuthData(BaseModel):
token: str
- dead_time: float
-
-
-class PlatformRuntimeInfo(BaseModel):
- stat_msg: str
- cpu_percent: str
- mem_percent: str
- disk_percent: str
- inte_send: str
- inte_recv: str
- boot_time: str
-
-
-class BotRuntimeInfo(BaseModel):
- cpu_percent: str
- mem_percent: str
- bot_run_time: str
+ md5: str
+ dead_time: int
class MessageDealerInfo(BaseModel):
@@ -29,11 +14,3 @@ class MessageDealerInfo(BaseModel):
total_r_m: str
total_d_m: str
total_f_m: str
-
-
-class ServiceInfo(BaseModel):
- service_name: str
- service_docs: str
- is_enabled: bool
- disable_user: list
- disable_group: list