diff options
Diffstat (limited to 'ATRI/plugins')
| -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 | 
7 files changed, 0 insertions, 526 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)]) - - -@router.get("/", response_model=models.Response) -async def _(): -    return models.Response(status=status.HTTP_200_OK, detail="控制台 API 路径", data=dict()) - - -@router.get("/auth", response_model=models.Response) -async def _(): -    return models.Response(status=status.HTTP_200_OK, detail="OK", data=dict()) - - -@router.websocket("/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 - - -@router.websocket("/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 - - -@router.get("/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) - - -@router.get("/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} - - -@router.get("/block/list", response_model=models.Response) -async def _(): -    return models.Response( -        status=status.HTTP_200_OK, detail="OK", data=_get_block_list() -    ) - - -@router.get("/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 - - -@scheduler.scheduled_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 | 
