mirror of
https://github.com/suyiiyii/nonebot-bison.git
synced 2026-05-09 18:27:56 +08:00
🚚 修改 nonebot_bison 项目结构 (#211)
* 🎨 修改 nonebot_bison 目录位置 * auto fix by pre-commit hooks * 🚚 fix frontend build target * 🚚 use soft link * Revert "🚚 use soft link" This reverts commit de21f79d5ae1bd5515b04f42a4138cb25ddf3e62. * 🚚 modify dockerfile --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: felinae98 <731499577@qq.com>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import re
|
||||
import sys
|
||||
from typing import Union
|
||||
|
||||
import nonebot
|
||||
from bs4 import BeautifulSoup as bs
|
||||
from nonebot.adapters.onebot.v11.message import MessageSegment
|
||||
from nonebot.log import default_format, logger
|
||||
from nonebot.plugin import require
|
||||
|
||||
from ..plugin_config import plugin_config
|
||||
from .context import ProcessContext
|
||||
from .http import http_client
|
||||
from .scheduler_config import SchedulerConfig, scheduler
|
||||
|
||||
__all__ = [
|
||||
"http_client",
|
||||
"Singleton",
|
||||
"parse_text",
|
||||
"ProcessContext",
|
||||
"html_to_text",
|
||||
"SchedulerConfig",
|
||||
"scheduler",
|
||||
]
|
||||
|
||||
|
||||
class Singleton(type):
|
||||
_instances = {}
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if cls not in cls._instances:
|
||||
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
|
||||
return cls._instances[cls]
|
||||
|
||||
|
||||
async def parse_text(text: str) -> MessageSegment:
|
||||
"return raw text if don't use pic, otherwise return rendered opcode"
|
||||
if plugin_config.bison_use_pic:
|
||||
require("nonebot_plugin_htmlrender")
|
||||
from nonebot_plugin_htmlrender import text_to_pic as _text_to_pic
|
||||
|
||||
return MessageSegment.image(await _text_to_pic(text))
|
||||
else:
|
||||
return MessageSegment.text(text)
|
||||
|
||||
|
||||
if not plugin_config.bison_skip_browser_check:
|
||||
require("nonebot_plugin_htmlrender")
|
||||
|
||||
|
||||
def html_to_text(html: str, query_dict: dict = {}) -> str:
|
||||
html = re.sub(r"<br\s*/?>", "<br>\n", html)
|
||||
html = html.replace("</p>", "</p>\n")
|
||||
soup = bs(html, "html.parser")
|
||||
if query_dict:
|
||||
node = soup.find(**query_dict)
|
||||
else:
|
||||
node = soup
|
||||
assert node is not None
|
||||
return node.text.strip()
|
||||
|
||||
|
||||
class Filter:
|
||||
def __init__(self) -> None:
|
||||
self.level: Union[int, str] = "DEBUG"
|
||||
|
||||
def __call__(self, record):
|
||||
module_name: str = record["name"]
|
||||
module = sys.modules.get(module_name)
|
||||
if module:
|
||||
module_name = getattr(module, "__module_name__", module_name)
|
||||
record["name"] = module_name.split(".")[0]
|
||||
levelno = (
|
||||
logger.level(self.level).no if isinstance(self.level, str) else self.level
|
||||
)
|
||||
nonebot_warning_level = logger.level("WARNING").no
|
||||
return (
|
||||
record["level"].no >= levelno
|
||||
if record["name"] != "nonebot"
|
||||
else record["level"].no >= nonebot_warning_level
|
||||
)
|
||||
|
||||
|
||||
if plugin_config.bison_filter_log:
|
||||
logger.remove()
|
||||
default_filter = Filter()
|
||||
logger.add(
|
||||
sys.stdout,
|
||||
colorize=True,
|
||||
diagnose=False,
|
||||
filter=default_filter,
|
||||
format=default_format,
|
||||
)
|
||||
config = nonebot.get_driver().config
|
||||
logger.success("Muted info & success from nonebot")
|
||||
default_filter.level = (
|
||||
("DEBUG" if config.debug else "INFO")
|
||||
if config.log_level is None
|
||||
else config.log_level
|
||||
)
|
||||
@@ -0,0 +1,40 @@
|
||||
from base64 import b64encode
|
||||
|
||||
from httpx import AsyncClient, Response
|
||||
|
||||
|
||||
class ProcessContext:
|
||||
reqs: list[Response]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.reqs = []
|
||||
|
||||
def log_response(self, resp: Response):
|
||||
self.reqs.append(resp)
|
||||
|
||||
def register_to_client(self, client: AsyncClient):
|
||||
async def _log_to_ctx(r: Response):
|
||||
self.log_response(r)
|
||||
|
||||
hooks = {
|
||||
"response": [_log_to_ctx],
|
||||
}
|
||||
client.event_hooks = hooks
|
||||
|
||||
def _should_print_content(self, r: Response) -> bool:
|
||||
content_type = r.headers["content-type"]
|
||||
if content_type.startswith("text"):
|
||||
return True
|
||||
if "json" in content_type:
|
||||
return True
|
||||
return False
|
||||
|
||||
def gen_req_records(self) -> list[str]:
|
||||
res = []
|
||||
for req in self.reqs:
|
||||
if self._should_print_content(req):
|
||||
log_content = f"{req.request.url} {req.request.headers} | [{req.status_code}] {req.headers} {req.text}"
|
||||
else:
|
||||
log_content = f"{req.request.url} {req.request.headers} | [{req.status_code}] {req.headers} b64encoded: {b64encode(req.content[:50]).decode()}"
|
||||
res.append(log_content)
|
||||
return res
|
||||
@@ -0,0 +1,106 @@
|
||||
""" 提供获取 Bot 的方法 """
|
||||
import random
|
||||
from typing import Any, Optional
|
||||
|
||||
import nonebot
|
||||
from nonebot import get_driver, on_notice
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Bot,
|
||||
FriendAddNoticeEvent,
|
||||
GroupDecreaseNoticeEvent,
|
||||
GroupIncreaseNoticeEvent,
|
||||
)
|
||||
|
||||
from ..types import User
|
||||
|
||||
GROUP: dict[int, list[Bot]] = {}
|
||||
USER: dict[int, list[Bot]] = {}
|
||||
|
||||
|
||||
def get_bots() -> list[Bot]:
|
||||
"""获取所有 OneBot 11 Bot"""
|
||||
bots = []
|
||||
for bot in nonebot.get_bots().values():
|
||||
if isinstance(bot, Bot):
|
||||
bots.append(bot)
|
||||
return bots
|
||||
|
||||
|
||||
async def refresh_bots():
|
||||
"""刷新缓存的 Bot 数据"""
|
||||
GROUP.clear()
|
||||
USER.clear()
|
||||
for bot in get_bots():
|
||||
# 获取群列表
|
||||
groups = await bot.get_group_list()
|
||||
for group in groups:
|
||||
group_id = group["group_id"]
|
||||
if group_id not in GROUP:
|
||||
GROUP[group_id] = [bot]
|
||||
else:
|
||||
GROUP[group_id].append(bot)
|
||||
|
||||
# 获取好友列表
|
||||
users = await bot.get_friend_list()
|
||||
for user in users:
|
||||
user_id = user["user_id"]
|
||||
if user_id not in USER:
|
||||
USER[user_id] = [bot]
|
||||
else:
|
||||
USER[user_id].append(bot)
|
||||
|
||||
|
||||
driver = get_driver()
|
||||
|
||||
|
||||
@driver.on_bot_connect
|
||||
@driver.on_bot_disconnect
|
||||
async def _(bot: Bot):
|
||||
await refresh_bots()
|
||||
|
||||
|
||||
change_notice = on_notice(priority=1)
|
||||
|
||||
|
||||
@change_notice.handle()
|
||||
async def _(bot: Bot, event: FriendAddNoticeEvent):
|
||||
await refresh_bots()
|
||||
|
||||
|
||||
# 01-06 16:56:51 [SUCCESS] nonebot | OneBot V11 **** | [notice.group_increase.approve]: {'time': 1672995411, 'self_id': ****, 'post_type': 'notice', 'notice_type': 'group_increase', 'sub_type': 'approve', 'user_id': ****, 'group_id': ****, 'operator_id': 0}
|
||||
# 01-06 16:58:09 [SUCCESS] nonebot | OneBot V11 **** | [notice.group_decrease.kick_me]: {'time': 1672995489, 'self_id': ****, 'post_type': 'notice', 'notice_type': 'group_decrease', 'sub_type': 'kick_me', 'user_id': ****, 'group_id': ****, 'operator_id': ****}
|
||||
@change_notice.handle()
|
||||
async def _(bot: Bot, event: GroupDecreaseNoticeEvent | GroupIncreaseNoticeEvent):
|
||||
if bot.self_id == event.user_id:
|
||||
await refresh_bots()
|
||||
|
||||
|
||||
def get_bot(user: User) -> Optional[Bot]:
|
||||
"""获取 Bot"""
|
||||
bots = []
|
||||
if user.user_type == "group":
|
||||
bots = GROUP.get(user.user, [])
|
||||
|
||||
if user.user_type == "private":
|
||||
bots = USER.get(user.user, [])
|
||||
|
||||
if not bots:
|
||||
return
|
||||
|
||||
return random.choice(bots)
|
||||
|
||||
|
||||
async def get_groups() -> list[dict[str, Any]]:
|
||||
"""获取所有群号"""
|
||||
all_groups: dict[int, dict[str, Any]] = {}
|
||||
for bot in get_bots():
|
||||
groups = await bot.get_group_list()
|
||||
all_groups.update(
|
||||
{
|
||||
group["group_id"]: group
|
||||
for group in groups
|
||||
if group["group_id"] not in all_groups
|
||||
}
|
||||
)
|
||||
|
||||
return list(all_groups.values())
|
||||
@@ -0,0 +1,24 @@
|
||||
import functools
|
||||
|
||||
import httpx
|
||||
|
||||
from ..plugin_config import plugin_config
|
||||
|
||||
http_args = {
|
||||
"proxies": plugin_config.bison_proxy or None,
|
||||
}
|
||||
http_headers = {"user-agent": plugin_config.bison_ua}
|
||||
|
||||
|
||||
@functools.wraps(httpx.AsyncClient)
|
||||
def http_client(*args, **kwargs):
|
||||
if headers := kwargs.get("headers"):
|
||||
new_headers = http_headers.copy()
|
||||
new_headers.update(headers)
|
||||
kwargs["headers"] = new_headers
|
||||
else:
|
||||
kwargs["headers"] = http_headers
|
||||
return httpx.AsyncClient(*args, **kwargs)
|
||||
|
||||
|
||||
http_client = functools.partial(http_client, **http_args)
|
||||
@@ -0,0 +1,38 @@
|
||||
from typing import Literal, Type
|
||||
|
||||
from httpx import AsyncClient
|
||||
|
||||
from ..types import Target
|
||||
from .http import http_client
|
||||
|
||||
|
||||
class SchedulerConfig:
|
||||
|
||||
schedule_type: Literal["date", "interval", "cron"]
|
||||
schedule_setting: dict
|
||||
name: str
|
||||
|
||||
def __str__(self):
|
||||
return f"[{self.name}]-{self.name}-{self.schedule_setting}"
|
||||
|
||||
def __init__(self):
|
||||
self.default_http_client = http_client()
|
||||
|
||||
async def get_client(self, target: Target) -> AsyncClient:
|
||||
return self.default_http_client
|
||||
|
||||
async def get_query_name_client(self) -> AsyncClient:
|
||||
return self.default_http_client
|
||||
|
||||
|
||||
def scheduler(
|
||||
schedule_type: Literal["date", "interval", "cron"], schedule_setting: dict
|
||||
) -> Type[SchedulerConfig]:
|
||||
return type(
|
||||
"AnonymousScheduleConfig",
|
||||
(SchedulerConfig,),
|
||||
{
|
||||
"schedule_type": schedule_type,
|
||||
"schedule_setting": schedule_setting,
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user