diff options
author | Komorebi <[email protected]> | 2023-10-01 02:18:25 +0800 |
---|---|---|
committer | Komorebi <[email protected]> | 2023-10-01 02:18:25 +0800 |
commit | 7cbe8a046f10c776b3c9ae80fd75b30a5d810f29 (patch) | |
tree | f03721b92a81bfd6208d462204b4ae99e9391800 | |
parent | cae8dfa08f870e463259521803426d2b3d169918 (diff) | |
download | ATRI-7cbe8a046f10c776b3c9ae80fd75b30a5d810f29.tar.gz ATRI-7cbe8a046f10c776b3c9ae80fd75b30a5d810f29.tar.bz2 ATRI-7cbe8a046f10c776b3c9ae80fd75b30a5d810f29.zip |
🔥 删除 console 插件
-rw-r--r-- | ATRI/plugins/console/__init__.py | 110 | ||||
-rw-r--r-- | ATRI/plugins/console/backend/__init__.py | 20 | ||||
-rw-r--r-- | ATRI/plugins/console/backend/api.py | 211 | ||||
-rw-r--r-- | ATRI/plugins/console/backend/models.py | 15 | ||||
-rw-r--r-- | ATRI/plugins/console/data_source.py | 93 | ||||
-rw-r--r-- | ATRI/plugins/console/listener.py | 61 | ||||
-rw-r--r-- | ATRI/plugins/console/models.py | 16 | ||||
-rw-r--r-- | README.md | 2 |
8 files changed, 1 insertions, 527 deletions
diff --git a/ATRI/plugins/console/__init__.py b/ATRI/plugins/console/__init__.py deleted file mode 100644 index 39fcc26..0000000 --- a/ATRI/plugins/console/__init__.py +++ /dev/null @@ -1,110 +0,0 @@ -from datetime import datetime - -from nonebot.params import ArgPlainText -from nonebot.adapters.onebot.v11 import PrivateMessageEvent, GroupMessageEvent - -from ATRI import conf -from ATRI.log import log -from ATRI.service import Service -from ATRI.permission import MASTER -from ATRI.message import MessageBuilder -from ATRI.utils.apscheduler import scheduler - -from .data_source import AuthDealer, get_host_ip - - -plugin = ( - Service("控制台") - .document("前端管理页面") - .only_admin(True) - .permission(MASTER) - .main_cmd("/con") -) - - -async def __del_auth_key(): - await AuthDealer.clear() - log.warning("控制台验证密钥已过期") - - -gen_console_key = plugin.cmd_as_group("auth", "获取进入网页后台的凭证") - - -@gen_console_key.got("is_pub_n", "咱的运行环境是否有公网(y/n)") -async def _(event: PrivateMessageEvent, is_pub_n: str = ArgPlainText("is_pub_n")): - is_access_key = conf.BotConfig.access_token - if not is_access_key: - await gen_console_key.finish( - MessageBuilder("缺少设置: access_token") - .text("请先填写该内容, 以保证ATRI与协议端链接的安全性") - .text("填写并重启, 方可启用控制台") - .text("Tip: 该内容请尽可能地复杂, 请勿使用中文") - ) - - 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 get_host_ip(False)) - await gen_console_key.send("没有公网吗...嗯知道了") - else: - host = str(await get_host_ip(True)) - port = conf.BotConfig.port - auth = AuthDealer() - data = await auth.store() - - msg = ( - MessageBuilder("控制台信息已生成!") - .text(f"请访问: {host}:{port}") - .text(f"Token: {auth.get_token()}") - .text("该 token 有效时间为 15min") - ) - - scheduler.add_job( - __del_auth_key, - name="清除后台验证凭证", - next_run_time=datetime.fromtimestamp(data.dead_time), - misfire_grace_time=15, - ) - - await gen_console_key.finish(msg) - - -@gen_console_key.handle() -async def _(event: GroupMessageEvent): - await gen_console_key.finish("请私戳咱获取(") - - -del_console_key = plugin.cmd_as_group("del", "销毁进入网页后台的凭证") - - -@del_console_key.got("is_sure_d", "...你确定吗(y/n)") -async def _(is_sure: str = ArgPlainText("is_sure_d")): - if is_sure != "y": - await del_console_key.finish("反悔了呢...") - - data = AuthDealer.get() - if data is None: - await del_console_key.finish("你还没向咱索取凭证呢...私戳咱键入 /con.auth 以获取") - - await AuthDealer.clear() - - await del_console_key.finish("销毁成功!如需再次获取: /con.auth") - - -import nonebot - -from .backend import app - -driver = nonebot.get_app() -driver.mount("/", app, name="test") diff --git a/ATRI/plugins/console/backend/__init__.py b/ATRI/plugins/console/backend/__init__.py deleted file mode 100644 index 4fb5a37..0000000 --- a/ATRI/plugins/console/backend/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -from fastapi import FastAPI - -# from fastapi.staticfiles import StaticFiles -from fastapi.middleware.cors import CORSMiddleware - -from .api import router as api_router - -# from ..data_source import FRONTEND_DIR - - -app = FastAPI( - title="Console for ATRI", - description="A admin UI controller for ATRI", -) - -app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["GET"]) - -app.include_router(api_router, prefix="/capi") - -# app.mount("/", StaticFiles(directory=FRONTEND_DIR, html=True), name="Console for ATRI") diff --git a/ATRI/plugins/console/backend/api.py b/ATRI/plugins/console/backend/api.py deleted file mode 100644 index 25e9da5..0000000 --- a/ATRI/plugins/console/backend/api.py +++ /dev/null @@ -1,211 +0,0 @@ -import os -import asyncio -from typing import Union -from datetime import datetime - -from fastapi import APIRouter, status, Depends, Query, HTTPException -from fastapi.websockets import WebSocket, WebSocketState -from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError - -from ATRI.utils import machine -from ATRI.service import SERVICES_DIR, ServiceInfo, ServiceTools - -from ..data_source import * -from ..listener import get_message_info -from . import models - - -def _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 "OK" - - -router = APIRouter(tags=["capi"], dependencies=[Depends(_author)]) - - [email protected]("/", response_model=models.Response) -async def _(): - return models.Response(status=status.HTTP_200_OK, detail="控制台 API 路径", data=dict()) - - [email protected]("/auth", response_model=models.Response) -async def _(): - return models.Response(status=status.HTTP_200_OK, detail="OK", data=dict()) - - [email protected]("/status") -async def _(websocket: WebSocket): - await websocket.accept() - - try: - while websocket.client_state == WebSocketState.CONNECTED: - await websocket.send_json( - models.Response( - status=status.HTTP_200_OK, - detail="OK", - data=models.StatusInfo( - platform=machine.get_platform_info().dict(), - cpu=machine.get_cpu_info().dict(), - mem=machine.get_mem_info().dict(), - disk=machine.get_disk_info().dict(), - net=machine.get_net_info().dict(), - ).dict(), - ).dict() - ) - await asyncio.sleep(2) - except ConnectionClosedOK: - pass - except ConnectionClosedError: - pass - finally: - await websocket.close() - return - - [email protected]("/status/message") -async def _(websocket: WebSocket): - await websocket.accept() - - try: - while websocket.client_state == WebSocketState.CONNECTED: - await websocket.send_json( - models.Response( - status=status.HTTP_200_OK, - detail="OK", - data=get_message_info().dict(), - ).dict() - ) - await asyncio.sleep(1) - except ConnectionClosedOK: - pass - except ConnectionClosedError: - pass - finally: - await websocket.close() - return - - [email protected]("/service/list", response_model=models.Response) -async def _(): - result = dict() - files = os.listdir(SERVICES_DIR) - for f in files: - if f == ".DS_Store": - continue - serv_path = SERVICES_DIR / f - data = ServiceInfo.parse_file(serv_path) - result[data.service] = data.dict() - - return models.Response(status=status.HTTP_200_OK, detail="OK", data=result) - - [email protected]("/service/edit", response_model=models.Response) -async def _( - service: str, - enabled: int, - for_global: int = 1, - user_id: int = int(), - group_id: int = int(), -): - msg = "OK" - data = ServiceTools(service).load_service() - if enabled and for_global: - data.enabled = True - elif not enabled and for_global: - data.enabled = False - - if user_id or group_id: - if enabled: - if user_id not in data.disable_user: - msg = "用户不存在于禁用名单" - else: - data.disable_user.remove(user_id) - - if group_id not in data.disable_group: - msg = "群不存在于禁用名单" - else: - data.disable_group.remove(group_id) - else: - if user_id in data.disable_user: - msg = "用户已存在于禁用名单" - else: - data.disable_user.append(user_id) - - if group_id in data.disable_group: - msg = "群已存在于禁用名单" - else: - data.disable_group.append(group_id) - - ServiceTools(service).save_service(data.dict()) - - return models.Response(status=status.HTTP_200_OK, detail=msg, data=dict()) - - -def _get_block_list(): - # 该处有一 typo - 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 {"user": user_data, "group": group_data} - - [email protected]("/block/list", response_model=models.Response) -async def _(): - return models.Response( - status=status.HTTP_200_OK, detail="OK", data=_get_block_list() - ) - - [email protected]("/block/edit", response_model=models.Response) -async def _(enabled: int, user_id: int = int(), group_id: int = int()): - data = _get_block_list() - now_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - msg = "OK" - if enabled: - if user_id: - if user_id in data["user"]: - msg = "用户已存在于黑名单" - else: - data["user"][user_id] = now_time - if group_id: - if group_id in data["group"]: - msg = "群已存在于黑名单" - else: - data["group"][group_id] = now_time - else: - if user_id: - if user_id not in data["user"]: - msg = "用户不存在于黑名单" - else: - del data["user"][user_id] - if group_id: - if group_id not in data["group"]: - msg = "群不存在于黑名单" - else: - del data["group"][group_id] - - 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 models.Response( - status=status.HTTP_200_OK, - detail=msg, - data=data, - ) diff --git a/ATRI/plugins/console/backend/models.py b/ATRI/plugins/console/backend/models.py deleted file mode 100644 index e7aeb54..0000000 --- a/ATRI/plugins/console/backend/models.py +++ /dev/null @@ -1,15 +0,0 @@ -from pydantic import BaseModel - - -class Response(BaseModel): - status: int - detail: str - data: dict - - -class StatusInfo(BaseModel): - platform: dict - cpu: dict - mem: dict - disk: dict - net: dict diff --git a/ATRI/plugins/console/data_source.py b/ATRI/plugins/console/data_source.py deleted file mode 100644 index cab951d..0000000 --- a/ATRI/plugins/console/data_source.py +++ /dev/null @@ -1,93 +0,0 @@ -import json -import socket -import string -import hashlib -from pathlib import Path -from random import sample -from typing import Optional -from datetime import datetime, timedelta - -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 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(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(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 = ( -# "https://guc.imki.moe/kyomotoi/Project-ATRI-Console/main/archive/dist.zip" -# ) - - -# async def init_resource(): -# log.info("控制台初始化中...") - -# try: -# resp = await request.get(__CONSOLE_RESOURCE_URL) -# except Exception: -# log.error("控制台资源装载失败, 将无法访问管理界面") -# return - -# file_path = CONSOLE_DIR / "dist.zip" -# with open(file_path, "wb") as w: -# w.write(resp.read()) - -# with zipfile.ZipFile(file_path, "r") as zr: -# zr.extractall(FRONTEND_DIR) - -# log.success("控制台初始化完成") diff --git a/ATRI/plugins/console/listener.py b/ATRI/plugins/console/listener.py deleted file mode 100644 index 1bd553c..0000000 --- a/ATRI/plugins/console/listener.py +++ /dev/null @@ -1,61 +0,0 @@ -from typing import Optional -from pydantic import BaseModel - -from nonebot.message import run_postprocessor - -from ATRI.utils.apscheduler import scheduler - - -interval_deal_message = int() -interval_recv_message = int() -interval_failed_message = int() - -total_deal_message = int() -total_recv_message = int() -total_failed_message = int() - - -@run_postprocessor -async def _(exception: Optional[Exception]): - global interval_deal_message, interval_recv_message, interval_failed_message - global total_deal_message, total_recv_message, total_failed_message - - if exception: - interval_failed_message += 1 - total_failed_message += 1 - else: - interval_deal_message += 1 - total_deal_message += 1 - - interval_recv_message += 1 - total_recv_message += 1 - - [email protected]_job("interval", name="消息统计数据重置", seconds=15, misfire_grace_time=30) -async def _(): - global interval_deal_message, interval_recv_message, interval_failed_message - - interval_deal_message = 0 - interval_recv_message = 0 - interval_failed_message = 0 - - -class MessageInfo(BaseModel): - interval_deal: int - interval_recv: int - interval_failed: int - - total_deal: int - total_recv: int - total_failed: int - - -def get_message_info() -> MessageInfo: - return MessageInfo( - interval_deal=interval_deal_message, - interval_recv=interval_recv_message, - interval_failed=interval_failed_message, - total_deal=total_deal_message, - total_recv=total_deal_message, - total_failed=total_failed_message, - ) diff --git a/ATRI/plugins/console/models.py b/ATRI/plugins/console/models.py deleted file mode 100644 index f7ddf8d..0000000 --- a/ATRI/plugins/console/models.py +++ /dev/null @@ -1,16 +0,0 @@ -from pydantic import BaseModel - - -class AuthData(BaseModel): - token: str - md5: str - dead_time: int - - -class MessageDealerInfo(BaseModel): - recv_msg: str - deal_msg: str - failed_deal_msg: str - total_r_m: str - total_d_m: str - total_f_m: str @@ -78,7 +78,7 @@ 请参考文档: [部署项目](https://atri.imki.moe/install/installation/) ## 📖 文档 | Documentation -所有公开的信息都可在 [atri.imki.moe](https://atri.imki.moe) 获取. 使用 [MkDocs](https://github.com/mkdocs/mkdocs/) 构建. +所有公开的信息都可在 [atri.imki.moe](https://atri.imki.moe) 获取. 使用 [VitePress](https://vitepress.dev/) 构建. [文档仓库](https://github.com/Kyomotoi/Project-ATRI-Docs) |