diff --git a/aunly_bbot/bot.py b/aunly_bbot/bot.py index beaf816..be3676e 100644 --- a/aunly_bbot/bot.py +++ b/aunly_bbot/bot.py @@ -96,3 +96,10 @@ else: saya.require("aunly_bbot.function") from . import function # noqa + + custom_path = Path("data", "custom") + custom_path.mkdir(parents=True, exist_ok=True) + for module in custom_path.glob("*"): + if module.name != "__pycache__": + logger.info(f"正在加载自定义模块:{module.stem}") + saya.require(f"data.custom.{module.stem}") diff --git a/aunly_bbot/cli/config.py b/aunly_bbot/cli/config.py index 03d15c3..736ea93 100644 --- a/aunly_bbot/cli/config.py +++ b/aunly_bbot/cli/config.py @@ -44,6 +44,7 @@ def __init__(self) -> None: self.bilibili_username() self.use_bilibili_login() self.wordcloud() + self.bcut_asr() self.event() self.webui() self.log_level() @@ -322,6 +323,21 @@ def wordcloud(self): else: self.config["Bilibili"]["use_wordcloud"] = False + def bcut_asr(self): + if ( + self.config["Bilibili"]["openai_summarization"] + or self.config["Bilibili"]["use_wordcloud"] + ): + bcut_asr = ListPrompt( + "是否使用 Bilibili ASR 进行视频内容语音识别?(用于 AI 总结和词云制作)", + [Choice("是(开启)"), Choice("否(关闭)")], + allow_filter=False, + annotation="使用键盘的 ↑ 和 ↓ 来选择, 按回车确认", + ).prompt() + self.config["Bilibili"]["use_bcut_asr"] = bcut_asr.name == "是(开启)" + else: + self.config["Bilibili"]["use_bcut_asr"] = False + def bilibili_concurrent(self): while True: concurrent = InputPrompt("请输入并发数(理论该值越大推送效率越高): ", default_text="10").prompt() diff --git a/aunly_bbot/function/command/content_resolve.py b/aunly_bbot/function/command/content_resolve.py index e3ab7ed..331fce3 100755 --- a/aunly_bbot/function/command/content_resolve.py +++ b/aunly_bbot/function/command/content_resolve.py @@ -5,10 +5,11 @@ from graia.saya import Channel from grpc.aio import AioRpcError from graia.ariadne.app import Ariadne -from graia.ariadne.model import Group from sentry_sdk import capture_exception from bilireq.exceptions import GrpcError +from graia.ariadne.model import Group, Member from httpx._exceptions import TimeoutException +from graia.broadcast.exceptions import ExecutionStop from graia.ariadne.event.message import GroupMessage from graia.ariadne.message.chain import MessageChain from graia.ariadne.message.element import Image, Source @@ -18,19 +19,21 @@ from ...model.exception import AbortError from ...utils.column_resolve import get_cv from ...core.data import ContentResolveData -from ...utils.text2image import rich_text2image from ...core.control import Interval, Permission from ...utils.video_subtitle import get_subtitle from ...utils.message_resolve import message_resolve from ...utils.draw_bili_image import binfo_image_create +from ...utils.text2image import rich_text2image, browser_text2image from ...utils.bilibili_request import get_b23_url, grpc_get_view_info -from ...utils.content_summarise import column_summarise, get_browser_image, subtitle_summarise +from ...utils.content_summarise import column_summarise, subtitle_summarise channel = Channel.current() @channel.use(ListenerSchema(listening_events=[GroupMessage], decorators=[Permission.require()])) -async def main(app: Ariadne, group: Group, message: MessageChain, source: Source): +async def main( + app: Ariadne, group: Group, member: Member, message: MessageChain, source: Source +): bili_number = await message_resolve(message) if not bili_number: return @@ -68,12 +71,12 @@ async def main(app: Ariadne, group: Group, message: MessageChain, source: Source title = video_info.activity_season.arc.title or video_info.arc.title archive_data = ContentResolveData(aid=aid) archive_data.title = title - await Interval.manual(aid + group.id) + await Interval.manual(aid + group.id, 30) try: logger.info(f"开始生成视频信息图片:{aid}") b23_url = await get_b23_url(f"https://www.bilibili.com/video/{bvid}") image = await binfo_image_create(video_info, b23_url) - await app.send_group_message( + info_message = await app.send_group_message( group, MessageChain( Image(data_bytes=image), @@ -90,6 +93,14 @@ async def main(app: Ariadne, group: Group, message: MessageChain, source: Source subtitle = await get_subtitle(aid, cid) archive_data.content = json.dumps(subtitle, ensure_ascii=False) + if ( + len(subtitle) < 10 + or video_info.arc.duration < BotConfig.Bilibili.asr_length_threshold + ): + raise AbortError("字幕内容过少且视频时长过短,跳过总结请求") + + chatgpt_thinks = True + async def openai_summarization(): logger.info(f"开始进行 AI 总结:{aid}") try: @@ -98,6 +109,12 @@ async def openai_summarization(): summarise = archive_data.openai else: logger.info(f"{aid} 总结不存在,正在尝试请求......") + try: + await Interval.manual(member, 600) + except ExecutionStop: + msg = f"{member.id} 在 10 分钟内已经请求过总结,跳过本次请求" + logger.info(msg) + raise AbortError(msg) ai_summary = await subtitle_summarise(subtitle, title) if ai_summary.summary: summarise = ai_summary.summary @@ -106,14 +123,21 @@ async def openai_summarization(): logger.warning(f"视频 {aid} 总结失败:{ai_summary.raw}") return + if summarise.lower().startswith("none"): + nonlocal chatgpt_thinks + chatgpt_thinks = False + raise AbortError("ChatGPT 认为这些字幕没有意义") + logger.debug(summarise) if BotConfig.Bilibili.use_browser: - image = await get_browser_image(summarise) + image = await browser_text2image(summarise) else: image = await rich_text2image(summarise) if image: await app.send_group_message( - group, MessageChain(Image(data_bytes=image)) + group, + MessageChain(Image(data_bytes=image)), + quote=info_message.source, ) except AbortError as e: logger.info(f"视频 {aid} 总结被终止:{e}") @@ -141,18 +165,18 @@ async def wordcloud(): wordcloud = await get_worldcloud_image(word_frequencies) if wordcloud: await app.send_group_message( - group, MessageChain(Image(data_bytes=wordcloud)) + group, + MessageChain(Image(data_bytes=wordcloud)), + quote=info_message.source, ) except Exception: capture_exception() logger.exception(f"视频 {aid} 词云出错") - gather = [] if BotConfig.Bilibili.openai_summarization: - gather.append(openai_summarization()) - if BotConfig.Bilibili.use_wordcloud: - gather.append(wordcloud()) - await asyncio.gather(*gather, return_exceptions=True) + await openai_summarization() + if BotConfig.Bilibili.use_wordcloud and chatgpt_thinks: + await wordcloud() except AbortError as e: logger.warning(f"视频 {aid} 总结失败:{e.message}") @@ -196,7 +220,7 @@ async def openai_summarization(): return if BotConfig.Bilibili.use_browser: - image = await get_browser_image(summarise) + image = await browser_text2image(summarise) else: image = await rich_text2image(summarise) if image: diff --git a/aunly_bbot/function/event/invited_join_group.py b/aunly_bbot/function/event/invited_join_group.py index d67c870..b13b944 100644 --- a/aunly_bbot/function/event/invited_join_group.py +++ b/aunly_bbot/function/event/invited_join_group.py @@ -46,5 +46,6 @@ async def main(app: Ariadne, event: BotInvitedJoinGroupRequestEvent): "该群不在白名单中,已拒绝加入", ), ) + await app.send_friend_message(event.supplicant, MessageChain("该群不在白名单中,已拒绝加入")) except UnknownTarget: logger.warning(f"由于未添加 {admin} 为好友,无法发送通知") diff --git a/aunly_bbot/model/bcut_asr.py b/aunly_bbot/model/bcut_asr.py new file mode 100644 index 0000000..e5f3acb --- /dev/null +++ b/aunly_bbot/model/bcut_asr.py @@ -0,0 +1,115 @@ +from enum import Enum +from pydantic import BaseModel + + +class ASRDataSeg(BaseModel): + "文字识别-断句" + + class ASRDataWords(BaseModel): + "文字识别-逐字" + label: str + start_time: int + end_time: int + confidence: int + + start_time: int + end_time: int + transcript: str + words: list[ASRDataWords] + confidence: int + + def to_srt_ts(self) -> str: + "转换为srt时间戳" + + def _conv(ms: int) -> tuple[int, int, int, int]: + return ms // 3600000, ms // 60000 % 60, ms // 1000 % 60, ms % 1000 + + s_h, s_m, s_s, s_ms = _conv(self.start_time) + e_h, e_m, e_s, e_ms = _conv(self.end_time) + return f"{s_h:02d}:{s_m:02d}:{s_s:02d},{s_ms:03d} --> {e_h:02d}:{e_m:02d}:{e_s:02d},{e_ms:03d}" + + def to_lrc_ts(self) -> str: + "转换为lrc时间戳" + + def _conv(ms: int) -> tuple[int, int, int]: + return ms // 60000, ms // 1000 % 60, ms % 1000 // 10 + + s_m, s_s, s_ms = _conv(self.start_time) + return f"[{s_m:02d}:{s_s:02d}.{s_ms:02d}]" + + +class ASRData(BaseModel): + "语音识别结果" + utterances: list[ASRDataSeg] + version: str + + def __iter__(self): + "iter穿透" + return iter(self.utterances) + + def has_data(self) -> bool: + "是否识别到数据" + return len(self.utterances) > 0 + + def to_txt(self) -> str: + "转成txt格式字幕 (无时间标记)" + return "\n".join(seg.transcript for seg in self.utterances) + + def to_srt(self) -> str: + "转成srt格式字幕" + return "\n".join( + f"{n}\n{seg.to_srt_ts()}\n{seg.transcript}\n" + for n, seg in enumerate(self.utterances, 1) + ) + + def to_lrc(self) -> str: + "转成lrc格式字幕" + return "\n".join(f"{seg.to_lrc_ts()}{seg.transcript}" for seg in self.utterances) + + def to_ass(self) -> str: + ... + + +class ResourceCreateRspSchema(BaseModel): + "上传申请响应" + resource_id: str + title: str + type: int + in_boss_key: str + size: int + upload_urls: list[str] + upload_id: str + per_size: int + + +class ResourceCompleteRspSchema(BaseModel): + "上传提交响应" + resource_id: str + download_url: str + + +class TaskCreateRspSchema(BaseModel): + "任务创建响应" + resource: str + result: str + task_id: str # 任务id + + +class ResultStateEnum(Enum): + "任务状态枚举" + STOP = 0 # 未开始 + RUNING = 1 # 运行中 + ERROR = 3 # 错误 + COMPLETE = 4 # 完成 + + +class ResultRspSchema(BaseModel): + "任务结果查询响应" + task_id: str # 任务id + result: str # 结果数据-json + remark: str # 任务状态详情 + state: ResultStateEnum # 任务状态 + + def parse(self) -> ASRData: + "解析结果数据" + return ASRData.parse_raw(self.result) diff --git a/aunly_bbot/model/config.py b/aunly_bbot/model/config.py index 4ea2e43..b115bde 100644 --- a/aunly_bbot/model/config.py +++ b/aunly_bbot/model/config.py @@ -59,6 +59,8 @@ class _Bilibili(BaseModel, extra=Extra.ignore): openai_model: str = "gpt-3.5-turbo" openai_proxy: Optional[AnyHttpUrl] = None use_wordcloud: bool = False + use_bcut_asr: bool = False + asr_length_threshold: int = 60 # 验证是否可以登录 @validator("use_login", always=True) diff --git a/aunly_bbot/static/bot_config.exp.yaml b/aunly_bbot/static/bot_config.exp.yaml index d9e13bd..1a17483 100644 --- a/aunly_bbot/static/bot_config.exp.yaml +++ b/aunly_bbot/static/bot_config.exp.yaml @@ -23,6 +23,8 @@ Bilibili: openai_model: "gpt-3.5-turbo-0301" # OpenAI 模型 openai_proxy: "http://localhost:7890" # 请求 OpenAI 所用的代理 use_wordcloud: true # 是否使用词云 + use_bcut_asr: true # 是否使用 BCut 进行 AI 语音识别 + asr_length_threshold: 60 # 调用语音识别的最小长度阈值(秒) Event: mute: true # 是否向管理员发送被禁言的事件提醒。 permchange: true # 是否向管理员发送权限变更的事件提醒。 diff --git a/aunly_bbot/test.py b/aunly_bbot/test.py index c75e6f1..a512687 100644 --- a/aunly_bbot/test.py +++ b/aunly_bbot/test.py @@ -11,8 +11,7 @@ from graia.ariadne.message.parser.twilight import Twilight, FullMatch from .core.control import Permission - -# from .utils.wordcloud import get_frequencies, get_wordcloud +from .utils.bilibili_request import grpc_get_playview channel = Channel.current() @@ -25,29 +24,4 @@ ) ) async def main(app: Ariadne, group: Group): - - sub = httpx.get( - "https://i0.hdslb.com/bfs/ai_subtitle/prod/31087008110492590207e0082656e40ce338d18289396914ffe" - ).json() - subs = [x["content"] for x in sub["body"]] - title = "" - description = "" - - word_counts = get_frequencies(subs) - word_cloud = await get_wordcloud(word_counts) - - # browser_context = app.launch_manager.get_interface(PlaywrightContext).context - # page = await browser_context.new_page() - # await page.set_viewport_size({"width": 460, "height": 100}) - # if req.error: - # print(req.message) - # return - # md = convert_md(req.summary) - # css = "\n".join(BuiltinCSS.github.value) - # await page.set_content( - # '
' - # f"{md}" - # ) - # result = await page.screenshot(full_page=True, type="jpeg", quality=95) - - await app.send_group_message(group, MessageChain(Image(data_bytes=word_cloud))) + print(await grpc_get_playview(439357034, 1076145927)) diff --git a/aunly_bbot/utils/bcut_asr.py b/aunly_bbot/utils/bcut_asr.py new file mode 100644 index 0000000..de8551a --- /dev/null +++ b/aunly_bbot/utils/bcut_asr.py @@ -0,0 +1,180 @@ +import time +import httpx + +from os import PathLike +from pathlib import Path +from loguru import logger +from typing import Literal, Optional + +from ..model.bcut_asr import ( + ResultRspSchema, + TaskCreateRspSchema, + ResourceCreateRspSchema, + ResourceCompleteRspSchema, +) + +__version__ = "0.0.2" + +API_REQ_UPLOAD = "https://member.bilibili.com/x/bcut/rubick-interface/resource/create" # 申请上传 +API_COMMIT_UPLOAD = ( + "https://member.bilibili.com/x/bcut/rubick-interface/resource/create/complete" # 提交上传 +) +API_CREATE_TASK = "https://member.bilibili.com/x/bcut/rubick-interface/task" # 创建任务 +API_QUERY_RESULT = "https://member.bilibili.com/x/bcut/rubick-interface/task/result" # 查询结果 + +SUPPORT_SOUND_FORMAT = Literal["flac", "aac", "m4a", "mp3", "wav"] + + +class APIError(Exception): + "接口调用错误" + + def __init__(self, code, msg) -> None: + self.code = code + self.msg = msg + super().__init__() + + def __str__(self) -> str: + return f"{self.code}:{self.msg}" + + +class BcutASR: + "必剪 语音识别接口" + session: httpx.AsyncClient + sound_name: str + sound_bin: bytes + sound_fmt: SUPPORT_SOUND_FORMAT + __in_boss_key: str + __resource_id: str + __upload_id: str + __upload_urls: list[str] + __per_size: int + __clips: int + __etags: list[str] + __download_url: str + task_id: str + + def __init__(self, file: Optional[str | PathLike] = None) -> None: + self.session = httpx.AsyncClient() + self.task_id = "" + self.__etags = [] + if file: + self.set_data(file) + + def set_data( + self, + file: Optional[str | PathLike] = None, + raw_data: Optional[bytes] = None, + data_fmt: Optional[SUPPORT_SOUND_FORMAT] = None, + ) -> None: + "设置欲识别的数据" + if file: + if not isinstance(file, (str, PathLike)): + raise TypeError("unknow file ptr") + # 文件类 + file = Path(file) + self.sound_bin = open(file, "rb").read() + suffix = data_fmt or file.suffix[1:] + self.sound_name = file.name + elif raw_data: + # bytes类 + self.sound_bin = raw_data + suffix = data_fmt + self.sound_name = f"{int(time.time())}.{suffix}" + else: + raise ValueError("none set data") + if suffix not in SUPPORT_SOUND_FORMAT.__args__: + raise TypeError("format is not support") + self.sound_fmt = suffix + logger.info(f"加载文件成功: {self.sound_name}") + + async def upload(self) -> None: + "申请上传" + if not self.sound_bin or not self.sound_fmt: + raise ValueError("none set data") + resp = await self.session.post( + API_REQ_UPLOAD, + data={ + "type": 2, + "name": self.sound_name, + "size": len(self.sound_bin), + "resource_file_type": self.sound_fmt, + "model_id": 7, + }, + ) + resp.raise_for_status() + resp = resp.json() + if code := resp["code"]: + raise APIError(code, resp["message"]) + resp_data = ResourceCreateRspSchema.parse_obj(resp["data"]) + self.__in_boss_key = resp_data.in_boss_key + self.__resource_id = resp_data.resource_id + self.__upload_id = resp_data.upload_id + self.__upload_urls = resp_data.upload_urls + self.__per_size = resp_data.per_size + self.__clips = len(resp_data.upload_urls) + logger.info( + f"申请上传成功, 总计大小{resp_data.size // 1024}KB, " + f"{self.__clips}分片, 分片大小{resp_data.per_size // 1024}KB: {self.__in_boss_key}" + ) + await self.__upload_part() + await self.__commit_upload() + + async def __upload_part(self) -> None: + "上传音频数据" + for clip in range(self.__clips): + start_range = clip * self.__per_size + end_range = (clip + 1) * self.__per_size + logger.info(f"开始上传分片{clip}: {start_range}-{end_range}") + resp = await self.session.put( + self.__upload_urls[clip], + data=self.sound_bin[start_range:end_range], + ) + resp.raise_for_status() + etag = resp.headers.get("Etag") + self.__etags.append(etag) + logger.info(f"分片{clip}上传成功: {etag}") + + async def __commit_upload(self) -> None: + "提交上传数据" + resp = await self.session.post( + API_COMMIT_UPLOAD, + data={ + "in_boss_key": self.__in_boss_key, + "resource_id": self.__resource_id, + "etags": ",".join(self.__etags), + "upload_id": self.__upload_id, + "model_id": 7, + }, + ) + resp.raise_for_status() + resp = resp.json() + if code := resp["code"]: + raise APIError(code, resp["message"]) + resp_data = ResourceCompleteRspSchema.parse_obj(resp["data"]) + self.__download_url = resp_data.download_url + logger.info("提交成功") + + async def create_task(self) -> str: + "开始创建转换任务" + resp = await self.session.post( + API_CREATE_TASK, json={"resource": self.__download_url, "model_id": "7"} + ) + resp.raise_for_status() + resp = resp.json() + if code := resp["code"]: + raise APIError(code, resp["message"]) + resp_data = TaskCreateRspSchema.parse_obj(resp["data"]) + self.task_id = resp_data.task_id + logger.info(f"任务已创建: {self.task_id}") + return self.task_id + + async def result(self, task_id: Optional[str] = None) -> ResultRspSchema: + "查询转换结果" + resp = await self.session.get( + API_QUERY_RESULT, params={"model_id": 7, "task_id": task_id or self.task_id} + ) + resp.raise_for_status() + resp = resp.json() + if code := resp["code"]: + raise APIError(code, resp["message"]) + return ResultRspSchema.parse_obj(resp["data"]) diff --git a/aunly_bbot/utils/bilibili_request.py b/aunly_bbot/utils/bilibili_request.py index dff9e00..5d61d02 100644 --- a/aunly_bbot/utils/bilibili_request.py +++ b/aunly_bbot/utils/bilibili_request.py @@ -9,7 +9,9 @@ from bilireq.grpc.protos.bilibili.app.view.v1.view_pb2 import ViewReq, ViewReply from bilireq.grpc.protos.bilibili.community.service.dm.v1.dm_pb2_grpc import DMStub from bilireq.grpc.protos.bilibili.app.dynamic.v2.dynamic_pb2_grpc import DynamicStub +from bilireq.grpc.protos.bilibili.app.playurl.v1.playurl_pb2_grpc import PlayURLStub from bilireq.grpc.protos.bilibili.community.service.dm.v1.dm_pb2 import DmViewReq, DmViewReply +from bilireq.grpc.protos.bilibili.app.playurl.v1.playurl_pb2 import PlayViewReq, PlayViewReply from bilireq.grpc.protos.bilibili.app.dynamic.v2.dynamic_pb2 import ( DynamicType, DynDetailsReq, @@ -152,3 +154,10 @@ async def grpc_get_dmview(pid: int, oid: int, type: int = 1, **kwargs) -> DmView stub = DMStub(kwargs.pop("_channel")) req = DmViewReq(pid=pid, oid=oid, type=type) return await stub.DmView(req, **kwargs) + + +@grpc_request +async def grpc_get_playview(aid: int, cid: int, **kwargs) -> PlayViewReply: + stub = PlayURLStub(kwargs.pop("_channel")) + req = PlayViewReq(aid=aid, cid=cid, qn=128, fnval=464) + return await stub.PlayView(req, **kwargs) diff --git a/aunly_bbot/utils/content_summarise.py b/aunly_bbot/utils/content_summarise.py index a996de5..dc1944c 100644 --- a/aunly_bbot/utils/content_summarise.py +++ b/aunly_bbot/utils/content_summarise.py @@ -1,12 +1,12 @@ import re from loguru import logger -from graia.ariadne.app import Ariadne from .openai import openai_req, get_small_size_transcripts, get_user_prompt, get_simple_prompt async def subtitle_summarise(sub: list[str], title: str): + """请求字幕总结""" small_size_transcripts = get_small_size_transcripts(sub) prompt = get_user_prompt(title, small_size_transcripts) logger.debug(prompt) @@ -14,26 +14,9 @@ async def subtitle_summarise(sub: list[str], title: str): async def column_summarise(cv_title: str, cv_text: str): + """请求专栏总结""" sentences = re.split(r"[,。;,.;\n]+", cv_text) small_size_transcripts = get_small_size_transcripts(sentences) prompt = get_user_prompt(cv_title, small_size_transcripts) logger.debug(prompt) return await openai_req(get_simple_prompt(prompt)) - - -async def get_browser_image(data: str): - from graiax.text2img.playwright import convert_md - from graiax.playwright.interface import PlaywrightContext - from graiax.text2img.playwright.renderer import BuiltinCSS - - app = Ariadne.current() - browser_context = app.launch_manager.get_interface(PlaywrightContext).context - page = await browser_context.new_page() - await page.set_viewport_size({"width": 400, "height": 100}) - md = convert_md(data) - css = "\n".join(BuiltinCSS.github.value) - await page.set_content( - '' - f"{md}" - ) - return await page.screenshot(full_page=True, type="jpeg", quality=95) diff --git a/aunly_bbot/utils/openai.py b/aunly_bbot/utils/openai.py index 5d4f96d..c43e091 100644 --- a/aunly_bbot/utils/openai.py +++ b/aunly_bbot/utils/openai.py @@ -16,7 +16,9 @@ if BotConfig.Bilibili.openai_summarization: logger.info("正在加载 OpenAI Token 计算模型") - tiktoken_enc = asyncio.run(tiktoken_async.encoding_for_model(BotConfig.Bilibili.openai_model)) + tiktoken_enc = asyncio.run( + tiktoken_async.encoding_for_model(BotConfig.Bilibili.openai_model) + ) logger.info(f"{tiktoken_enc.name} 加载成功") @@ -27,10 +29,12 @@ def get_user_prompt(title: str, transcript: str) -> str: prompt = ( "Your output should use the following template:\n## Summary\n## Highlights\n" "- [Emoji] Bulletpoint\n\n" - "Your task is to summarise the video I have given you in up to 6 concise bullet points, " + "Your task is to summarise the video I have given you in up to 2 to 6 concise bullet points, " "starting with a short highlight, each bullet point is at least 15 words. " "Choose an appropriate emoji for each bullet point. " f"Use the video above: {{Title}} {{Transcript}}." + "If you think the content in the transcript is meaningless or nonsensical, " + "you can choose to skip summarization and simply output 'none'." f"\n\nReply in {language} Language." ) return f'Title: "{title}"\nTranscript: "{transcript}"\n\nInstructions: {prompt}' @@ -73,14 +77,16 @@ def get_simple_prompt(prompt: str): async def openai_req( - prompt_message: list[dict[str, str]], token: Optional[str] = None + prompt_message: list[dict[str, str]], + token: Optional[str] = BotConfig.Bilibili.openai_api_token, + model: str = BotConfig.Bilibili.openai_model, ) -> AISummary: - if not (token or BotConfig.Bilibili.openai_api_token): + if not token: return AISummary(error=True, message="未配置 OpenAI API Token") async with httpx.AsyncClient( proxies=BotConfig.Bilibili.openai_proxy, headers={ - "Authorization": f"Bearer {token or BotConfig.Bilibili.openai_api_token}", + "Authorization": f"Bearer {token}", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)" " Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.69", }, @@ -89,11 +95,11 @@ async def openai_req( req = await client.post( "https://api.openai.com/v1/chat/completions", json={ - "model": "gpt-3.5-turbo", + "model": model, "messages": prompt_message, }, ) if req.status_code != 200: return AISummary(error=True, message=req.text, raw=req.json()) - logger.info(f"OpenAI Response token 实际: {req.json()['usage']}") + logger.info(f"[OpenAI] Response token 实际: {req.json()['usage']}") return AISummary(summary=req.json()["choices"][0]["message"]["content"], raw=req.json()) diff --git a/aunly_bbot/utils/text2image.py b/aunly_bbot/utils/text2image.py index daddcd0..4e0979b 100644 --- a/aunly_bbot/utils/text2image.py +++ b/aunly_bbot/utils/text2image.py @@ -2,6 +2,7 @@ from io import BytesIO from pathlib import Path +from graia.ariadne.app import Ariadne from PIL import Image, ImageFont, ImageDraw from .strings import get_cut_str @@ -69,3 +70,21 @@ async def rich_text2image(data: str): bio = BytesIO() image.convert("RGB").save(bio, "jpeg", optimize=True) return bio.getvalue() + + +async def browser_text2image(data: str): + from graiax.text2img.playwright import convert_md + from graiax.playwright.interface import PlaywrightContext + from graiax.text2img.playwright.renderer import BuiltinCSS + + app = Ariadne.current() + browser_context = app.launch_manager.get_interface(PlaywrightContext).context + page = await browser_context.new_page() + await page.set_viewport_size({"width": 400, "height": 100}) + md = convert_md(data) + css = "\n".join(BuiltinCSS.github.value) + await page.set_content( + '' + f"{md}" + ) + return await page.screenshot(full_page=True, type="jpeg", quality=95) diff --git a/aunly_bbot/utils/video_subtitle.py b/aunly_bbot/utils/video_subtitle.py index bb039f6..abdb4c4 100644 --- a/aunly_bbot/utils/video_subtitle.py +++ b/aunly_bbot/utils/video_subtitle.py @@ -1,16 +1,26 @@ +import httpx +import asyncio + from loguru import logger +from typing import Optional +from sentry_sdk import capture_exception +from ..core.bot_config import BotConfig from ..model.exception import AbortError +from ..model.bcut_asr import ResultStateEnum + +from .bcut_asr import BcutASR from .bilibili_request import get_player, hc +from .bilibili_request import grpc_get_playview -async def get_subtitle_url(aid: int, cid: int) -> str: +async def get_subtitle_url(aid: int, cid: int) -> Optional[str]: video_player = await get_player(aid, cid) subtitles_raw: list[dict] = video_player["subtitle"]["subtitles"] logger.debug(subtitles_raw) if not subtitles_raw: - raise AbortError("未找到字幕") + return logger.debug(subtitles_raw) ai_subtitles = {} @@ -35,10 +45,54 @@ async def get_subtitle_url(aid: int, cid: int) -> str: async def get_subtitle(aid: int, cid: int) -> list[str]: subtitle_url = await get_subtitle_url(aid, cid) - logger.debug(subtitle_url) - subtitle = await hc.get(f"https:{subtitle_url}") - if subtitle.status_code != 200: - logger.warning(f"字幕获取失败:{aid} {cid},状态码:{subtitle.status_code},内容:{subtitle.text}") - raise AbortError("字幕下载失败") - logger.info(f"字幕获取成功:{aid} {cid}") + if subtitle_url: + logger.debug(subtitle_url) + subtitle = await hc.get(f"https:{subtitle_url}") + if subtitle.status_code != 200: + logger.warning(f"字幕获取失败:{aid} {cid},状态码:{subtitle.status_code},内容:{subtitle.text}") + raise AbortError("字幕下载失败") + logger.info(f"字幕获取成功:{aid} {cid}") + elif BotConfig.Bilibili.use_bcut_asr: + logger.info(f"字幕获取失败,尝试使用 BCut-ASR:{aid} {cid}") + playview = await grpc_get_playview(aid, cid) + if not playview.video_info.dash_audio: + raise AbortError("视频无音频流") + async with httpx.AsyncClient( + headers={ + "user-agent": "Bilibili Freedoooooom/MarkII", + }, + ) as client: + audio_resp = await client.get( + playview.video_info.dash_audio[-1].backup_url[0] + if playview.video_info.dash_audio[-1].backup_url + else playview.video_info.dash_audio[-1].baseUrl, + ) + audio_resp.raise_for_status() + audio = audio_resp.content + try: + asr = await get_bcut_asr(audio) + except Exception as e: + logger.exception("BCut-ASR 识别失败") + capture_exception() + raise AbortError("BCut-ASR 识别失败") from e + return [x.transcript for x in asr] + else: + raise AbortError("未找到字幕且未开启 AI 语音识别") return [x["content"] for x in subtitle.json()["body"]] + + +async def get_bcut_asr(file_bytes: bytes): + bcut = BcutASR() + bcut.set_data(raw_data=file_bytes, data_fmt="m4a") + await bcut.upload() + await bcut.create_task() + while True: + result = await bcut.result() + if result.state == ResultStateEnum.COMPLETE: + return result.parse() + elif result.state in [ResultStateEnum.RUNING, ResultStateEnum.STOP]: + logger.info(f"[BCut-ASR] 任务 {result.task_id} 正在进行中 - {result.state}...") + await asyncio.sleep(2) + elif result.state == ResultStateEnum.ERROR: + logger.error(f"[BCut-ASR] 任务 {result.task_id} 发生错误!") + raise AbortError("语音识别出错") diff --git a/pdm.lock b/pdm.lock index 6bf04d9..83a3aec 100644 --- a/pdm.lock +++ b/pdm.lock @@ -67,7 +67,7 @@ dependencies = [ [[package]] name = "black" -version = "23.1.0" +version = "23.3.0" requires_python = ">=3.7" summary = "The uncompromising code formatter." dependencies = [ @@ -214,7 +214,7 @@ dependencies = [ [[package]] name = "fonttools" -version = "4.39.2" +version = "4.39.3" requires_python = ">=3.8" summary = "Tools to manipulate font files" @@ -588,7 +588,7 @@ summary = "A small Python package for determining appropriate platform-specific [[package]] name = "playwright" -version = "1.32.0" +version = "1.32.1" requires_python = ">=3.7" summary = "A high-level API to automate web browsers" dependencies = [ @@ -778,7 +778,7 @@ dependencies = [ [[package]] name = "sentry-sdk" -version = "1.17.0" +version = "1.18.0" summary = "Python client for Sentry (https://sentry.io)" dependencies = [ "certifi", @@ -787,7 +787,7 @@ dependencies = [ [[package]] name = "setuptools" -version = "67.6.0" +version = "67.6.1" requires_python = ">=3.7" summary = "Easily download, build, install, upgrade, and uninstall Python packages" @@ -1054,32 +1054,32 @@ content_hash = "sha256:c892e190d7b11d45f530a1d2a22c5ad1e5ebc2e57c3a524c775e89844 {url = "https://files.pythonhosted.org/packages/46/79/c1de23be337d15bfde499dfbcc96c87629ed62bfbb946063f32ff673e2a3/bilireq-0.2.4-py3-none-any.whl", hash = "sha256:b5396164b27657cac8008c6a2d9bf6a8b5414d92d2b27cb5bee074f1fd0b5b0b"}, {url = "https://files.pythonhosted.org/packages/bb/1f/39c400c31ed32e2da8a9f0dfd9ecd3f0ff57e9f328d1dcfd91828c0e6882/bilireq-0.2.4.tar.gz", hash = "sha256:0c52c7c1fbe1f1b9d8b53ab9a6d4fbae363f6d6dc35c6a7cf1cc8652a10e2e00"}, ] -"black 23.1.0" = [ - {url = "https://files.pythonhosted.org/packages/01/8a/065d0a59c1ebe13186b12a2fa3965a41fc1588828709995e2630004d216e/black-23.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8178318cb74f98bc571eef19068f6ab5613b3e59d4f47771582f04e175570ed8"}, - {url = "https://files.pythonhosted.org/packages/15/11/533355217b1cc4a6df3263048060c1527f733d4720e158de2085293112bb/black-23.1.0.tar.gz", hash = "sha256:b0bd97bea8903f5a2ba7219257a44e3f1f9d00073d6cc1add68f0beec69692ac"}, - {url = "https://files.pythonhosted.org/packages/18/99/bb1be0ff3a7e912679ad234a3c4884fa7689dfcc4eae85bddb6c04feaa62/black-23.1.0-py3-none-any.whl", hash = "sha256:7a0f701d314cfa0896b9001df70a530eb2472babb76086344e688829efd97d32"}, - {url = "https://files.pythonhosted.org/packages/20/de/eff8e3ccc22b5c2be1265a9e61f1006d03e194519a3ca2e83dd8483dbbb5/black-23.1.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:49f7b39e30f326a34b5c9a4213213a6b221d7ae9d58ec70df1c4a307cf2a1580"}, - {url = "https://files.pythonhosted.org/packages/2d/9a/a81bf384a08d8a5e13d97223a60a74ac3c16c0aecdbd85edbc662d158bde/black-23.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:9afd3f493666a0cd8f8df9a0200c6359ac53940cbde049dcb1a7eb6ee2dd7074"}, - {url = "https://files.pythonhosted.org/packages/32/a7/1d207427b87780c505a41c9430d26362e729954503b8ffba27c4f53a6810/black-23.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bf649fda611c8550ca9d7592b69f0637218c2369b7744694c5e4902873b2f3a"}, - {url = "https://files.pythonhosted.org/packages/3d/dc/12dc29bb38b8db68c79b8339de1590fe1ae796858bfa6cf7494eb672be21/black-23.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b70eb40a78dfac24842458476135f9b99ab952dd3f2dab738c1881a9b38b753"}, - {url = "https://files.pythonhosted.org/packages/3e/c0/abc7031d670d211e4e2a063910d587dfcb62ce469631e779b23b66653442/black-23.1.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:57c18c5165c1dbe291d5306e53fb3988122890e57bd9b3dcb75f967f13411a26"}, - {url = "https://files.pythonhosted.org/packages/43/bc/5232fd6b0fd6d6177140cfb7d8f0f0e06638e2a750122767e265beb91161/black-23.1.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:a29650759a6a0944e7cca036674655c2f0f63806ddecc45ed40b7b8aa314b651"}, - {url = "https://files.pythonhosted.org/packages/6b/d1/4394e4b0a24ad0f556aca3ab11e27f2e199f03b43f147c31a4befbf62b48/black-23.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:121ca7f10b4a01fd99951234abdbd97728e1240be89fde18480ffac16503d481"}, - {url = "https://files.pythonhosted.org/packages/77/11/db2ae5bf93af5185086d9b1baf4ce369ca34c3a01835230873aa9163d52d/black-23.1.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:bb460c8561c8c1bec7824ecbc3ce085eb50005883a6203dcfb0122e95797ee06"}, - {url = "https://files.pythonhosted.org/packages/7e/fe/6c05c3f9255b7b498cfb88faa85b45329f1b7b0ecb444ebdc6b74ffa1457/black-23.1.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c1c476bc7b7d021321e7d93dc2cbd78ce103b84d5a4cf97ed535fbc0d6660648"}, - {url = "https://files.pythonhosted.org/packages/96/af/3361b34907efbfd9d55af453488be2282f831d98b7d201248b38d4c44346/black-23.1.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:a8471939da5e824b891b25751955be52ee7f8a30a916d570a5ba8e0f2eb2ecad"}, - {url = "https://files.pythonhosted.org/packages/9a/ee/549e8be7f635cabcc3c7c3f2c3b27971dc32735155631b9ef2dcb1bd861f/black-23.1.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:bfffba28dc52a58f04492181392ee380e95262af14ee01d4bc7bb1b1c6ca8d27"}, - {url = "https://files.pythonhosted.org/packages/a4/ec/934e89820289e6952922fa5965aec0e046ed65da168ffb0515af1e3364e1/black-23.1.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:a59db0a2094d2259c554676403fa2fac3473ccf1354c1c63eccf7ae65aac8ab6"}, - {url = "https://files.pythonhosted.org/packages/ae/93/1e62fe94ab531bdc3f6cbbbd5b518727277bf40f695777b4097db5da2a38/black-23.1.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c91dfc2c2a4e50df0026f88d2215e166616e0c80e86004d0003ece0488db2739"}, - {url = "https://files.pythonhosted.org/packages/b1/7e/c368e9c795387a01bc181d8acbfd178278cc9960c5e7ef1059222a4419f9/black-23.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:a436e7881d33acaf2536c46a454bb964a50eff59b21b51c6ccf5a40601fbef24"}, - {url = "https://files.pythonhosted.org/packages/b7/33/8e074fd8b86a1c8668f5493ed28929d87bdccb6aa68c2975b47a02f92287/black-23.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a951cc83ab535d248c89f300eccbd625e80ab880fbcfb5ac8afb5f01a258ac9"}, - {url = "https://files.pythonhosted.org/packages/be/f9/11e401323cd5b4e53d138fc880564765466a86acd2d4b50d7c8cdd048c18/black-23.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6663f91b6feca5d06f2ccd49a10f254f9298cc1f7f49c46e498a0771b507104"}, - {url = "https://files.pythonhosted.org/packages/c0/1d/8dac412cf5cc4120a438969a4fafefdc3de8fa13d411f317a9f9f1e268a4/black-23.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0680d4380db3719ebcfb2613f34e86c8e6d15ffeabcf8ec59355c5e7b85bb555"}, - {url = "https://files.pythonhosted.org/packages/cf/fe/dda4b7eedb9d4dc46e306b814f7838cd9026907fdc889f75eb9f6d47d414/black-23.1.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:9880d7d419bb7e709b37e28deb5e68a49227713b623c72b2b931028ea65f619b"}, - {url = "https://files.pythonhosted.org/packages/d0/cb/0a38ffdafbb4b3f337adaf1b79aeaf4b8a21ed18835acad6349e46c78c80/black-23.1.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:b6a92a41ee34b883b359998f0c8e6eb8e99803aa8bf3123bf2b2e6fec505a221"}, - {url = "https://files.pythonhosted.org/packages/dd/19/875b5006e40ea69a4120671f50317100b24732f2b877203722c91bc4eef3/black-23.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:162e37d49e93bd6eb6f1afc3e17a3d23a823042530c37c3c42eeeaf026f38468"}, - {url = "https://files.pythonhosted.org/packages/e6/0a/9a5fca4a2ca07d4dbc3b00445c9353f05ea182b000f68c9ad6ba1da87a47/black-23.1.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:0052dba51dec07ed029ed61b18183942043e00008ec65d5028814afaab9a22fd"}, - {url = "https://files.pythonhosted.org/packages/f1/89/ccc28cb74a66c094b609295b009b5e0350c10b75661d2450eeed2f60ce37/black-23.1.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:382998821f58e5c8238d3166c492139573325287820963d2f7de4d518bd76958"}, +"black 23.3.0" = [ + {url = "https://files.pythonhosted.org/packages/06/1e/273d610249f0335afb1ddb03664a03223f4826e3d1a95170a0142cb19fb4/black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, + {url = "https://files.pythonhosted.org/packages/12/4b/99c71d1cf1353edd5aff2700b8960f92e9b805c9dab72639b67dbb449d3a/black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, + {url = "https://files.pythonhosted.org/packages/13/0a/ed8b66c299e896780e4528eed4018f5b084da3b9ba4ee48328550567d866/black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, + {url = "https://files.pythonhosted.org/packages/13/25/cfa06788d0a936f2445af88f13604b5bcd5c9d050db618c718e6ebe66f74/black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, + {url = "https://files.pythonhosted.org/packages/21/14/d5a2bec5fb15f9118baab7123d344646fac0b1c6939d51c2b05259cd2d9c/black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, + {url = "https://files.pythonhosted.org/packages/24/eb/2d2d2c27cb64cfd073896f62a952a802cd83cf943a692a2f278525b57ca9/black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, + {url = "https://files.pythonhosted.org/packages/27/70/07aab2623cfd3789786f17e051487a41d5657258c7b1ef8f780512ffea9c/black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, + {url = "https://files.pythonhosted.org/packages/29/b1/b584fc863c155653963039664a592b3327b002405043b7e761b9b0212337/black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, + {url = "https://files.pythonhosted.org/packages/3c/d7/85f3d79f9e543402de2244c4d117793f262149e404ea0168841613c33e07/black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, + {url = "https://files.pythonhosted.org/packages/3f/0d/81dd4194ce7057c199d4f28e4c2a885082d9d929e7a55c514b23784f7787/black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, + {url = "https://files.pythonhosted.org/packages/49/36/15d2122f90ff1cd70f06892ebda777b650218cf84b56b5916a993dc1359a/black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, + {url = "https://files.pythonhosted.org/packages/49/d7/f3b7da6c772800f5375aeb050a3dcf682f0bbeb41d313c9c2820d0156e4e/black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, + {url = "https://files.pythonhosted.org/packages/69/49/7e1f0cf585b0d607aad3f971f95982cc4208fc77f92363d632d23021ee57/black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, + {url = "https://files.pythonhosted.org/packages/6d/b4/0f13ab7f5e364795ff82b76b0f9a4c9c50afda6f1e2feeb8b03fdd7ec57d/black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, + {url = "https://files.pythonhosted.org/packages/ad/e7/4642b7f462381799393fbad894ba4b32db00870a797f0616c197b07129a9/black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, + {url = "https://files.pythonhosted.org/packages/c0/53/42e312c17cfda5c8fc4b6b396a508218807a3fcbb963b318e49d3ddd11d5/black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, + {url = "https://files.pythonhosted.org/packages/ca/44/eb41edd3f558a6139f09eee052dead4a7a464e563b822ddf236f5a8ee286/black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, + {url = "https://files.pythonhosted.org/packages/ce/f4/2b0c6ac9e1f8584296747f66dd511898b4ebd51d6510dba118279bff53b6/black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, + {url = "https://files.pythonhosted.org/packages/d1/6e/5810b6992ed70403124c67e8b3f62858a32b35405177553f1a78ed6b6e31/black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, + {url = "https://files.pythonhosted.org/packages/d6/36/66370f5017b100225ec4950a60caeef60201a10080da57ddb24124453fba/black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, + {url = "https://files.pythonhosted.org/packages/d7/6f/d3832960a3b646b333b7f0d80d336a3c123012e9d9d5dba4a622b2b6181d/black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, + {url = "https://files.pythonhosted.org/packages/db/f4/7908f71cc71da08df1317a3619f002cbf91927fb5d3ffc7723905a2113f7/black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, + {url = "https://files.pythonhosted.org/packages/de/b4/76f152c5eb0be5471c22cd18380d31d188930377a1a57969073b89d6615d/black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, + {url = "https://files.pythonhosted.org/packages/eb/a5/17b40bfd9b607b69fa726b0b3a473d14b093dcd5191ea1a1dd664eccfee3/black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, + {url = "https://files.pythonhosted.org/packages/fd/5b/fc2d7922c1a6bb49458d424b5be71d251f2d0dc97be9534e35d171bdc653/black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, ] "certifi 2022.12.7" = [ {url = "https://files.pythonhosted.org/packages/37/f7/2b1b0ec44fdc30a3d31dfebe52226be9ddc40cd6c0f34ffc8923ba423b69/certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, @@ -1349,9 +1349,9 @@ content_hash = "sha256:c892e190d7b11d45f530a1d2a22c5ad1e5ebc2e57c3a524c775e89844 {url = "https://files.pythonhosted.org/packages/66/53/3ad4a3b74d609b3b9008a10075c40e7c8909eae60af53623c3888f7a529a/flake8-6.0.0.tar.gz", hash = "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181"}, {url = "https://files.pythonhosted.org/packages/d9/6a/bb0122ebe280476c924470779d2595f1403878cafe3c8a343ac56a5a9c0e/flake8-6.0.0-py2.py3-none-any.whl", hash = "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7"}, ] -"fonttools 4.39.2" = [ - {url = "https://files.pythonhosted.org/packages/0b/f9/fbd1287671d35f8984be7952d61c0d968df9794e2d5009513424201d9264/fonttools-4.39.2.zip", hash = "sha256:e2d9f10337c9e3b17f9bce17a60a16a885a7d23b59b7f45ce07ea643e5580439"}, - {url = "https://files.pythonhosted.org/packages/0c/e6/77183fd874c8f6162a1c8f3b91a96ad910d10aef010a51a4db8c2f96f3dd/fonttools-4.39.2-py3-none-any.whl", hash = "sha256:85245aa2fd4cf502a643c9a9a2b5a393703e150a6eaacc3e0e84bb448053f061"}, +"fonttools 4.39.3" = [ + {url = "https://files.pythonhosted.org/packages/16/07/1c7547e27f559ec078801d522cc4d5127cdd4ef8e831c8ddcd9584668a07/fonttools-4.39.3-py3-none-any.whl", hash = "sha256:64c0c05c337f826183637570ac5ab49ee220eec66cf50248e8df527edfa95aeb"}, + {url = "https://files.pythonhosted.org/packages/39/d7/ab05ae34dd57dd657e492d95ce7ec6bfebfb3bfcdc7316660ac5a13fcfee/fonttools-4.39.3.zip", hash = "sha256:9234b9f57b74e31b192c3fc32ef1a40750a8fbc1cd9837a7b7bfc4ca4a5c51d7"}, ] "frozenlist 1.3.3" = [ {url = "https://files.pythonhosted.org/packages/01/a3/a3c18bfd7bd2a56831b985140f98eb6dda684a2d8b2a54b1077b45c7f9d9/frozenlist-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8"}, @@ -2033,14 +2033,14 @@ content_hash = "sha256:c892e190d7b11d45f530a1d2a22c5ad1e5ebc2e57c3a524c775e89844 {url = "https://files.pythonhosted.org/packages/15/04/3f882b46b454ab374ea75425c6f931e499150ec1385a73e55b3f45af615a/platformdirs-3.2.0.tar.gz", hash = "sha256:d5b638ca397f25f979350ff789db335903d7ea010ab28903f57b27e1b16c2b08"}, {url = "https://files.pythonhosted.org/packages/b2/f3/4fb5fae710fc9f22a42cd90dc0547da18ec83e2e139294ab94f04c449cf5/platformdirs-3.2.0-py3-none-any.whl", hash = "sha256:ebe11c0d7a805086e99506aa331612429a72ca7cd52a1f0d277dc4adc20cb10e"}, ] -"playwright 1.32.0" = [ - {url = "https://files.pythonhosted.org/packages/02/d1/80a8a7cd2360b86b74183084c98fc36eab9aada4a75d97be52815f876741/playwright-1.32.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:1cccf2185452cdd7eb4c130fad15ed403fb246d3f6560c6c0ff65953e6b204e7"}, - {url = "https://files.pythonhosted.org/packages/13/a4/a619d189c48aaf52829769c2d02ea2e1680c666a1be7b4c1a3ab70bcf59d/playwright-1.32.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef7dcf45b7011105e33055e8cb3aa9bbaa50a861e8cc2d997f132379b721fe29"}, - {url = "https://files.pythonhosted.org/packages/1a/90/7abc358715136dd502bfb14e116a7e3cdf01f45235a53c5f0b6427ca0970/playwright-1.32.0-py3-none-win_amd64.whl", hash = "sha256:138f1671e0033218e2d83bcd1a89000ee913fb84adff4f3606cfeb86529db0c8"}, - {url = "https://files.pythonhosted.org/packages/1b/08/2fe957fad6b79291ff82e2e1748683a257f7ba3854c13a7ff0ea6e765d68/playwright-1.32.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b94b014442989dd707b7c4f4c8cc92462d087282a48b121055343a3229f1333f"}, - {url = "https://files.pythonhosted.org/packages/66/b7/67bbbdcc70b1cb3ba848ca270327812fd07579efef148fe66358387e94c1/playwright-1.32.0-py3-none-win32.whl", hash = "sha256:ad58e165b6cb0125f9176637176a1b46db7c8360a6058ae44ca1c5ff24740c26"}, - {url = "https://files.pythonhosted.org/packages/6d/37/4acc780608816bdbae650b71de316a7cf26f4cd451e5bd1b931303eef620/playwright-1.32.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:0d92de3ee2652de48ba05737493687dc07bd721415afb528b5fa909950291e67"}, - {url = "https://files.pythonhosted.org/packages/c8/7d/b8c0d1e6e446704522773be63f1807af22390a796c9cc7b06e20d366907d/playwright-1.32.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:0c824f29d7406860c6c6773c512018c9ec2114d0baef20968e994e9cb3498dfa"}, +"playwright 1.32.1" = [ + {url = "https://files.pythonhosted.org/packages/14/78/9404632712b0b2e4c182acee42ee10a6eef7147be8cec4693522300cfe8f/playwright-1.32.1-py3-none-win32.whl", hash = "sha256:274bfdd413a979346ce66e99c993c105a123e48da591a65638e5cdf518c90172"}, + {url = "https://files.pythonhosted.org/packages/1c/ad/4cce7baf3b36e73a9c47264b6ab129642e592499056605a1d0a15c253dfa/playwright-1.32.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d56a743f7d88a313b25a88422779c64e5d5a95baa805b9dfd1c5785aa01d217d"}, + {url = "https://files.pythonhosted.org/packages/4b/b7/705759b492b69c757c662d4ce6c881c6856fa909e317c123bfb356a3c5a0/playwright-1.32.1-py3-none-macosx_11_0_universal2.whl", hash = "sha256:42473495f8af0279d868cc541d0c6d3733a8adb117253499dae85203104b0824"}, + {url = "https://files.pythonhosted.org/packages/6f/28/a2d138d740c560d62277a8b4c9b3330064ee9484507678c15e0f7d3f77fb/playwright-1.32.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5dbf28b8256c2f570a66d6a7c04cd0bfb5225e696e01f85cf5aa49e29ea95b42"}, + {url = "https://files.pythonhosted.org/packages/92/f6/e4e64f8787ad8b47357b3d57aab61dea0757311d6397ac403ef2b9a44d71/playwright-1.32.1-py3-none-win_amd64.whl", hash = "sha256:32bb5645904b5ba3096a4696c70ce3213eb2310c77273140dc5de14498a84134"}, + {url = "https://files.pythonhosted.org/packages/c8/2b/e2f7e616e8c04af1a6922cc40182680239a5d76ec235aa4d063080f6ad72/playwright-1.32.1-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:e2f919e8611f598d6e81bd12ab24c5987955b05fc663c98b862034a955387300"}, + {url = "https://files.pythonhosted.org/packages/d2/b6/e6c73bc0ac5d3cf62ca2a9e3e9bbee49131411fd4414e867c37cf55512db/playwright-1.32.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:83123330e2913a28d11bb8846f7c81a4736553c80f3e9748d213bcaa24fafe91"}, ] "prompt-toolkit 3.0.38" = [ {url = "https://files.pythonhosted.org/packages/4b/bb/75cdcd356f57d17b295aba121494c2333d26bfff1a837e6199b8b83c415a/prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"}, @@ -2283,13 +2283,13 @@ content_hash = "sha256:c892e190d7b11d45f530a1d2a22c5ad1e5ebc2e57c3a524c775e89844 {url = "https://files.pythonhosted.org/packages/49/97/fa78e3d2f65c02c8e1268b9aba606569fe97f6c8f7c2d74394553347c145/rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, {url = "https://files.pythonhosted.org/packages/aa/65/7d973b89c4d2351d7fb232c2e452547ddfa243e93131e7cfa766da627b52/rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, ] -"sentry-sdk 1.17.0" = [ - {url = "https://files.pythonhosted.org/packages/3e/f1/4bfd190f07f49f823eb9fe55b23c8be8a21aa9b9190df6d27721052fd046/sentry-sdk-1.17.0.tar.gz", hash = "sha256:ad40860325c94d1a656da70fba5a7c4dbb2f6809d3cc2d00f74ca0b608330f14"}, - {url = "https://files.pythonhosted.org/packages/e5/c5/669e528a5ccb16c22ef239a8dc9e51c855a2bcea548e8d71808ce1438045/sentry_sdk-1.17.0-py2.py3-none-any.whl", hash = "sha256:3c4e898f7a3edf5a2042cd0dcab6ee124e2112189228c272c08ad15d3850c201"}, +"sentry-sdk 1.18.0" = [ + {url = "https://files.pythonhosted.org/packages/6c/3d/ae41e28b30705fb0e8e8ef557d1fbf954b4f63bf0e5df6ac71797b6e2ea2/sentry_sdk-1.18.0-py2.py3-none-any.whl", hash = "sha256:714203a9adcac4a4a35e348dc9d3e294ad0200a66cdca26c068967d728f34fcb"}, + {url = "https://files.pythonhosted.org/packages/e0/1a/5e6119102fe3c09b2b5bce8997793283ccbeeb3d2fd1db2fed69d74e4fa4/sentry-sdk-1.18.0.tar.gz", hash = "sha256:d07b9569a151033b462f7a7113ada94cc41ecf49daa83d35f5f852a0b9cf3b44"}, ] -"setuptools 67.6.0" = [ - {url = "https://files.pythonhosted.org/packages/25/f3/d68c20919bc774c6cb127f1762f2f2f999d700a58198556e883dd3700e58/setuptools-67.6.0.tar.gz", hash = "sha256:2ee892cd5f29f3373097f5a814697e397cf3ce313616df0af11231e2ad118077"}, - {url = "https://files.pythonhosted.org/packages/c3/9e/8a7ba2c9984a060daa6c6f9fff4d576b7ace3936239d6b771541eab972ed/setuptools-67.6.0-py3-none-any.whl", hash = "sha256:b78aaa36f6b90a074c1fa651168723acbf45d14cb1196b6f02c0fd07f17623b2"}, +"setuptools 67.6.1" = [ + {url = "https://files.pythonhosted.org/packages/0b/fc/8781442def77b0aa22f63f266d4dadd486ebc0c5371d6290caf4320da4b7/setuptools-67.6.1-py3-none-any.whl", hash = "sha256:e728ca814a823bf7bf60162daf9db95b93d532948c4c0bea762ce62f60189078"}, + {url = "https://files.pythonhosted.org/packages/cb/46/22ec35f286a77e6b94adf81b4f0d59f402ed981d4251df0ba7b992299146/setuptools-67.6.1.tar.gz", hash = "sha256:257de92a9d50a60b8e22abfcbb771571fde0dbf3ec234463212027a4eeecbe9a"}, ] "six 1.16.0" = [ {url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, diff --git a/pyproject.toml b/pyproject.toml index 264c99b..a1458e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "aunly-bbot" -version = "1.3.0" +version = "1.3.1" description = "一个用于 QQ 群内高效推送哔哩哔哩 UP 动态及直播的机器人" readme = "readme.md" keywords = ["graia", "graiax", "bilibili", "qqbot", "grpc", "playwright", "fastapi", "bot", "openai", "chatgpt"] diff --git a/requirements.txt b/requirements.txt index e09f180..ce71210 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ async-timeout==4.0.2 attrs==22.2.0 bcrypt==4.0.1 bilireq==0.2.4 -black==23.1.0 +black==23.3.0 certifi==2022.12.7 cffi==1.15.1 charset-normalizer==3.1.0 @@ -21,7 +21,7 @@ ecdsa==0.18.0 emoji==2.2.0 fastapi==0.95.0 flake8==6.0.0 -fonttools==4.39.2 +fonttools==4.39.3 frozenlist==1.3.3 graia-amnesia==0.7.1 graia-ariadne==0.11.2 @@ -60,7 +60,7 @@ pathspec==0.11.1 peewee==3.16.0 pillow==9.4.0 platformdirs==3.2.0 -playwright==1.32.0 +playwright==1.32.1 prompt-toolkit==3.0.38 protobuf==4.22.1 psutil==5.9.4 @@ -83,8 +83,8 @@ rfc3986==1.5.0 rich==13.3.3 richuru==0.1.1 rsa==4.9 -sentry-sdk==1.17.0 -setuptools==67.6.0 +sentry-sdk==1.18.0 +setuptools==67.6.1 six==1.16.0 sniffio==1.3.0 starlette==0.26.1