使大部分功能可用、修改部分配置

This commit is contained in:
Wuyi无疑 2023-02-20 23:27:20 +08:00
parent 706bb59abc
commit eb8789c823
13 changed files with 196 additions and 103 deletions

2
.gitignore vendored
View File

@ -663,3 +663,5 @@ FodyWeavers.xsd
# End of https://www.toptal.com/developers/gitignore/api/visualstudio,visualstudiocode,jetbrains+all,python,data
config.json
res_data
GsData.db

View File

@ -41,7 +41,7 @@ poetry run python core.py
import asyncio
from gsuid_core.sv import SL, SV
from gsuid_core.server import Bot
from gsuid_core.bot import Bot
from gsuid_core.models import MessageContent
@ -52,7 +52,7 @@ async def get_switch_msg(bot: Bot, msg: MessageContent):
im = await process(name) # 自己的业务逻辑
await bot.logger.info('正在进行[关闭/开启开关]') # 发送loger
await bot.send(im) # 发送消息
sv=SV(
name='复杂的服务', # 定义一组服务`开关`,
permission=3, # 权限 0为master1为superuser2为群的群主&管理员3为普通

49
gsuid_core/bot.py Normal file
View File

@ -0,0 +1,49 @@
import asyncio
from typing import List, Union, Optional
from logger import logger
from fastapi import WebSocket
from gs_logger import GsLogger
from segment import MessageSegment
from msgspec import json as msgjson
from models import Message, MessageSend
class Bot:
def __init__(self, _id: str, ws: WebSocket):
self.bot_id = _id
self.bot = ws
self.logger = GsLogger(self.bot_id, ws)
self.queue = asyncio.queues.Queue()
self.background_tasks = set()
self.user_id: Optional[str] = None
self.group_id: Optional[str] = None
self.user_type: Optional[str] = None
async def send(self, message: Union[Message, List[Message], str, bytes]):
if isinstance(message, Message):
message = [message]
elif isinstance(message, str):
if message.startswith('base64://'):
message = [MessageSegment.image(message)]
else:
message = [MessageSegment.text(message)]
elif isinstance(message, bytes):
message = [MessageSegment.image(message)]
send = MessageSend(
content=message,
bot_id=self.bot_id,
target_type=self.user_type,
target_id=self.group_id if self.group_id else self.user_id,
)
logger.info(f'[发送消息] {send}')
await self.bot.send_bytes(msgjson.encode(send))
async def _process(self):
while True:
data = await self.queue.get()
task = asyncio.create_task(data)
self.background_tasks.add(task)
task.add_done_callback(
lambda _: self.background_tasks.discard(task)
)

View File

@ -36,8 +36,9 @@ class GsClient:
intent = await self._input()
msg = MessageReceive(
bot_id='Nonebot',
user_type='group',
group_id='123456789',
user_type='DIRECT',
user_pm=2,
group_id=None,
user_id='5253123',
content=[Message(type='text', data=intent)],
)

View File

@ -1,7 +1,7 @@
import asyncio
import uvicorn
from server import gss
from gss import gss
from config import core_config
from handler import handle_event
from models import MessageReceive

42
gsuid_core/gs_logger.py Normal file
View File

@ -0,0 +1,42 @@
from typing import Literal
from fastapi import WebSocket
from models import MessageSend
from segment import MessageSegment
from msgspec import json as msgjson
class GsLogger:
def __init__(self, bot_id: str, ws: WebSocket):
self.bot_id = bot_id
self.bot = ws
def get_msg_send(
self, type: Literal['INFO', 'WARNING', 'ERROR', 'SUCCESS'], msg: str
):
return MessageSend(
content=[MessageSegment.log(type, msg)],
bot_id=self.bot_id,
target_type=None,
target_id=None,
)
async def info(self, msg: str):
await self.bot.send_bytes(
msgjson.encode(self.get_msg_send('INFO', msg))
)
async def warning(self, msg: str):
await self.bot.send_bytes(
msgjson.encode(self.get_msg_send('WARNING', msg))
)
async def error(self, msg: str):
await self.bot.send_bytes(
msgjson.encode(self.get_msg_send('ERROR', msg))
)
async def success(self, msg: str):
await self.bot.send_bytes(
msgjson.encode(self.get_msg_send('SUCCESS', msg))
)

6
gsuid_core/gss.py Normal file
View File

@ -0,0 +1,6 @@
from server import GsServer
gss = GsServer()
if not gss.is_load:
gss.is_load = True
gss.load_plugins()

View File

@ -1,6 +1,6 @@
import asyncio
from server import Bot
from bot import Bot
from trigger import Trigger
from config import core_config
from models import MessageContent, MessageReceive
@ -50,6 +50,11 @@ async def handle_event(ws: Bot, msg: MessageReceive):
SL.lst[sv].enabled
and user_pm <= SL.lst[sv].permission
and msg.group_id not in SL.lst[sv].black_list
and True
if SL.lst[sv].area == 'ALL'
or (msg.group_id and SL.lst[sv].area == 'GROUP')
or (not msg.group_id and SL.lst[sv].area == 'DIRECT')
else False
)
]
await asyncio.gather(*pending, return_exceptions=True)

19
gsuid_core/hook.py Normal file
View File

@ -0,0 +1,19 @@
from functools import wraps
from typing import Callable, Optional
from gsuid_core.server import gss
from gsuid_core.logger import logger
def on_bot_connect():
def deco(func: Callable) -> Callable:
gss.bot_connect_def.append(func)
@wraps(func)
async def wrapper(*args, **kwargs) -> Optional[Callable]:
logger.info('@on_bot_connect已成功调用...')
return await func(*args, **kwargs)
return wrapper
return deco

22
gsuid_core/logger.py Normal file
View File

@ -0,0 +1,22 @@
import logging
import rollbar
from rollbar.logger import RollbarHandler
# Initialize Rollbar SDK with your server-side access token
rollbar.init(
'ACCESS_TOKEN',
environment='staging',
handler='async',
)
# Set root logger to log DEBUG and above
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# Report ERROR and above to Rollbar
rollbar_handler = RollbarHandler()
rollbar_handler.setLevel(logging.ERROR)
# Attach Rollbar handler to the root logger
logger.addHandler(rollbar_handler)

View File

@ -8,19 +8,19 @@ class Message(Struct):
data: Optional[Any] = None
class MessageReceive(Struct):
class MessageReceive(Struct, frozen=True):
bot_id: str = 'Bot'
user_type: Optional[str] = None
group_id: Optional[str] = None
user_id: Optional[str] = None
user_id: str = ''
user_pm: int = 3
content: List[Message] = []
class MessageContent(Struct):
raw: Optional[MessageReceive] = None
raw: MessageReceive = MessageReceive()
raw_text: str = ''
command: Optional[str] = None
command: str = ''
text: str = ''
image: Optional[str] = None
at: Optional[str] = None

View File

@ -2,94 +2,31 @@ import sys
import asyncio
import importlib
from pathlib import Path
from typing import Dict, List, Union, Literal, Optional
from typing import Dict, Callable
from bot import Bot
from logger import logger
from fastapi import WebSocket
from segment import MessageSegment
from msgspec import json as msgjson
from models import Message, MessageSend
sys.path.append(str(Path(__file__).parents[1]))
class Bot:
def __init__(self, _id: str):
self.bot_id = _id
self.bot = gss.active_bot[_id]
self.logger = GsLogger(self.bot_id)
self.queue = asyncio.queues.Queue()
self.background_tasks = set()
self.user_id: Optional[str] = None
self.group_id: Optional[str] = None
self.user_type: Optional[str] = None
async def send(self, message: Union[Message, List[Message], str]):
if isinstance(message, Message):
message = [message]
elif isinstance(message, str):
if message.startswith('base64://'):
message = [MessageSegment.image(message)]
else:
message = [MessageSegment.text(message)]
send = MessageSend(
content=message,
bot_id=self.bot_id,
target_type=self.user_type,
target_id=self.group_id if self.group_id else self.user_id,
)
print(f'[发送消息] {send}')
await self.bot.send_bytes(msgjson.encode(send))
async def _process(self):
while True:
data = await self.queue.get()
task = asyncio.create_task(data)
self.background_tasks.add(task)
task.add_done_callback(
lambda _: self.background_tasks.discard(task)
)
class GsLogger:
def __init__(self, bot_id: str):
self.bot_id = bot_id
self.bot = gss.active_bot[bot_id]
def get_msg_send(
self, type: Literal['INFO', 'WARNING', 'ERROR', 'SUCCESS'], msg: str
):
return MessageSend(
content=[MessageSegment.log(type, msg)],
bot_id=self.bot_id,
target_type=None,
target_id=None,
)
async def info(self, msg: str):
await self.bot.send_bytes(
msgjson.encode(self.get_msg_send('INFO', msg))
)
async def warning(self, msg: str):
await self.bot.send_bytes(
msgjson.encode(self.get_msg_send('WARNING', msg))
)
async def error(self, msg: str):
await self.bot.send_bytes(
msgjson.encode(self.get_msg_send('ERROR', msg))
)
async def success(self, msg: str):
await self.bot.send_bytes(
msgjson.encode(self.get_msg_send('SUCCESS', msg))
)
class GsServer:
_instance = None
is_initialized = False
is_load = False
bot_connect_def = set()
def __new__(cls, *args, **kwargs):
# 判断sv是否已经被初始化
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
self.active_bot: Dict[str, WebSocket] = {}
self.load_plugins()
if not self.is_initialized:
self.active_bot: Dict[str, WebSocket] = {}
self.is_initialized = True
def load_plugins(self):
sys.path.append(str(Path(__file__).parents[1]))
@ -121,12 +58,14 @@ class GsServer:
async def connect(self, websocket: WebSocket, bot_id: str) -> Bot:
await websocket.accept()
self.active_bot[bot_id] = websocket
print(f'{bot_id}已连接!')
return Bot(bot_id)
logger.info(f'{bot_id}已连接!')
_task = [_def() for _def in self.bot_connect_def]
asyncio.gather(*_task)
return Bot(bot_id, websocket)
def disconnect(self, bot_id: str):
del self.active_bot[bot_id]
print(f'{bot_id}已中断!')
logger.warning(f'{bot_id}已中断!')
async def send(self, message: str, bot_id: str):
await self.active_bot[bot_id].send_text(message)
@ -135,5 +74,7 @@ class GsServer:
for bot_id in self.active_bot:
await self.send(message, bot_id)
gss = GsServer()
@classmethod
def on_bot_connect(cls, func: Callable):
cls.bot_connect_def.add(func)
return func

View File

@ -3,6 +3,7 @@ from __future__ import annotations
from functools import wraps
from typing import Dict, List, Tuple, Union, Literal, Callable, Optional
from logger import logger
from trigger import Trigger
from config import core_config
@ -23,7 +24,7 @@ config_sv = core_config.get_config('sv')
class SV:
is_initialized = False
def __new__(cls, *args):
def __new__(cls, *args, **kwargs):
# 判断sv是否已经被初始化
if args[0] in SL.lst:
return SL.lst[args[0]]
@ -34,14 +35,15 @@ class SV:
def __init__(
self,
name: str,
name: str = '',
permission: int = 3,
priority: int = 5,
enabled: bool = True,
area: Literal['GROUP', 'DIRECT', 'ALL'] = 'ALL',
black_list: List = [],
):
if not self.is_initialized:
print(f'{name}】模块初始化中...')
logger.info(f'{name}】模块初始化中...')
# sv名称重复的sv名称将被并入一个sv里
self.name: str = name
# sv内包含的触发器
@ -54,21 +56,25 @@ class SV:
self.enabled = config_sv[name]['enabled']
self.permission = config_sv[name]['permission']
self.black_list = config_sv[name]['black_list']
self.area = config_sv[name]['area']
else:
# sv优先级
self.priority: int = priority
self.priority = priority
# sv是否开启
self.enabled: bool = enabled
self.enabled = enabled
# 黑名单群
self.black_list: List = black_list
self.black_list = black_list
# 权限 0为master1为superuser2为群的群主&管理员3为普通
self.permission: int = permission
self.permission = permission
# 作用范围
self.area = area
# 写入
self.set(
priority=priority,
enabled=enabled,
permission=permission,
black_list=black_list,
area=area,
)
def set(self, **kwargs):
@ -96,7 +102,7 @@ class SV:
keyword_list = (keyword,)
for _k in keyword_list:
if _k not in self.TL:
print(f'载入{type}触发器【{_k}】!')
logger.info(f'载入{type}触发器【{_k}】!')
self.TL[_k] = Trigger(type, _k, func)
@wraps(func)