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

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 # End of https://www.toptal.com/developers/gitignore/api/visualstudio,visualstudiocode,jetbrains+all,python,data
config.json config.json
res_data
GsData.db

View File

@ -41,7 +41,7 @@ poetry run python core.py
import asyncio import asyncio
from gsuid_core.sv import SL, SV 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 from gsuid_core.models import MessageContent

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() intent = await self._input()
msg = MessageReceive( msg = MessageReceive(
bot_id='Nonebot', bot_id='Nonebot',
user_type='group', user_type='DIRECT',
group_id='123456789', user_pm=2,
group_id=None,
user_id='5253123', user_id='5253123',
content=[Message(type='text', data=intent)], content=[Message(type='text', data=intent)],
) )

View File

@ -1,7 +1,7 @@
import asyncio import asyncio
import uvicorn import uvicorn
from server import gss from gss import gss
from config import core_config from config import core_config
from handler import handle_event from handler import handle_event
from models import MessageReceive 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 import asyncio
from server import Bot from bot import Bot
from trigger import Trigger from trigger import Trigger
from config import core_config from config import core_config
from models import MessageContent, MessageReceive from models import MessageContent, MessageReceive
@ -50,6 +50,11 @@ async def handle_event(ws: Bot, msg: MessageReceive):
SL.lst[sv].enabled SL.lst[sv].enabled
and user_pm <= SL.lst[sv].permission and user_pm <= SL.lst[sv].permission
and msg.group_id not in SL.lst[sv].black_list 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) 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 data: Optional[Any] = None
class MessageReceive(Struct): class MessageReceive(Struct, frozen=True):
bot_id: str = 'Bot' bot_id: str = 'Bot'
user_type: Optional[str] = None user_type: Optional[str] = None
group_id: Optional[str] = None group_id: Optional[str] = None
user_id: Optional[str] = None user_id: str = ''
user_pm: int = 3 user_pm: int = 3
content: List[Message] = [] content: List[Message] = []
class MessageContent(Struct): class MessageContent(Struct):
raw: Optional[MessageReceive] = None raw: MessageReceive = MessageReceive()
raw_text: str = '' raw_text: str = ''
command: Optional[str] = None command: str = ''
text: str = '' text: str = ''
image: Optional[str] = None image: Optional[str] = None
at: Optional[str] = None at: Optional[str] = None

View File

@ -2,94 +2,31 @@ import sys
import asyncio import asyncio
import importlib import importlib
from pathlib import Path 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 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])) 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: 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): def __init__(self):
if not self.is_initialized:
self.active_bot: Dict[str, WebSocket] = {} self.active_bot: Dict[str, WebSocket] = {}
self.load_plugins() self.is_initialized = True
def load_plugins(self): def load_plugins(self):
sys.path.append(str(Path(__file__).parents[1])) sys.path.append(str(Path(__file__).parents[1]))
@ -121,12 +58,14 @@ class GsServer:
async def connect(self, websocket: WebSocket, bot_id: str) -> Bot: async def connect(self, websocket: WebSocket, bot_id: str) -> Bot:
await websocket.accept() await websocket.accept()
self.active_bot[bot_id] = websocket self.active_bot[bot_id] = websocket
print(f'{bot_id}已连接!') logger.info(f'{bot_id}已连接!')
return Bot(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): def disconnect(self, bot_id: str):
del self.active_bot[bot_id] del self.active_bot[bot_id]
print(f'{bot_id}已中断!') logger.warning(f'{bot_id}已中断!')
async def send(self, message: str, bot_id: str): async def send(self, message: str, bot_id: str):
await self.active_bot[bot_id].send_text(message) await self.active_bot[bot_id].send_text(message)
@ -135,5 +74,7 @@ class GsServer:
for bot_id in self.active_bot: for bot_id in self.active_bot:
await self.send(message, bot_id) await self.send(message, bot_id)
@classmethod
gss = GsServer() 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 functools import wraps
from typing import Dict, List, Tuple, Union, Literal, Callable, Optional from typing import Dict, List, Tuple, Union, Literal, Callable, Optional
from logger import logger
from trigger import Trigger from trigger import Trigger
from config import core_config from config import core_config
@ -23,7 +24,7 @@ config_sv = core_config.get_config('sv')
class SV: class SV:
is_initialized = False is_initialized = False
def __new__(cls, *args): def __new__(cls, *args, **kwargs):
# 判断sv是否已经被初始化 # 判断sv是否已经被初始化
if args[0] in SL.lst: if args[0] in SL.lst:
return SL.lst[args[0]] return SL.lst[args[0]]
@ -34,14 +35,15 @@ class SV:
def __init__( def __init__(
self, self,
name: str, name: str = '',
permission: int = 3, permission: int = 3,
priority: int = 5, priority: int = 5,
enabled: bool = True, enabled: bool = True,
area: Literal['GROUP', 'DIRECT', 'ALL'] = 'ALL',
black_list: List = [], black_list: List = [],
): ):
if not self.is_initialized: if not self.is_initialized:
print(f'{name}】模块初始化中...') logger.info(f'{name}】模块初始化中...')
# sv名称重复的sv名称将被并入一个sv里 # sv名称重复的sv名称将被并入一个sv里
self.name: str = name self.name: str = name
# sv内包含的触发器 # sv内包含的触发器
@ -54,21 +56,25 @@ class SV:
self.enabled = config_sv[name]['enabled'] self.enabled = config_sv[name]['enabled']
self.permission = config_sv[name]['permission'] self.permission = config_sv[name]['permission']
self.black_list = config_sv[name]['black_list'] self.black_list = config_sv[name]['black_list']
self.area = config_sv[name]['area']
else: else:
# sv优先级 # sv优先级
self.priority: int = priority self.priority = priority
# sv是否开启 # sv是否开启
self.enabled: bool = enabled self.enabled = enabled
# 黑名单群 # 黑名单群
self.black_list: List = black_list self.black_list = black_list
# 权限 0为master1为superuser2为群的群主&管理员3为普通 # 权限 0为master1为superuser2为群的群主&管理员3为普通
self.permission: int = permission self.permission = permission
# 作用范围
self.area = area
# 写入 # 写入
self.set( self.set(
priority=priority, priority=priority,
enabled=enabled, enabled=enabled,
permission=permission, permission=permission,
black_list=black_list, black_list=black_list,
area=area,
) )
def set(self, **kwargs): def set(self, **kwargs):
@ -96,7 +102,7 @@ class SV:
keyword_list = (keyword,) keyword_list = (keyword,)
for _k in keyword_list: for _k in keyword_list:
if _k not in self.TL: if _k not in self.TL:
print(f'载入{type}触发器【{_k}】!') logger.info(f'载入{type}触发器【{_k}】!')
self.TL[_k] = Trigger(type, _k, func) self.TL[_k] = Trigger(type, _k, func)
@wraps(func) @wraps(func)