From de9d0f00e79d5b66f330ed0adc2410849d2b6977 Mon Sep 17 00:00:00 2001 From: cypas Date: Thu, 4 Jul 2024 20:55:21 +0800 Subject: [PATCH 01/17] =?UTF-8?q?perf:qq=5Fmd=E6=B7=BB=E5=8A=A0=E5=AE=98?= =?UTF-8?q?=E6=96=B9=E7=BE=A4=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/handle/qq_md.py | 82 +++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/nonebot_plugin_splatoon3_nso/handle/qq_md.py b/nonebot_plugin_splatoon3_nso/handle/qq_md.py index 3fd4b20..46ec812 100644 --- a/nonebot_plugin_splatoon3_nso/handle/qq_md.py +++ b/nonebot_plugin_splatoon3_nso/handle/qq_md.py @@ -19,6 +19,9 @@ def last_md(user_id, image_size: tuple, url: str) -> QQ_Msg: button_show3 = "/last c" button_cmd3 = "/last c" + button_show4 = "bot官方群" + button_cmd4 = "http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=zGefDQ4GQYFPAB-hFkeFLlyQ8qbG5S2w&authKey=j0b9yXmtSzYry6qQQ%2FFXxw7U%2Fp6kXyET0xj%2BRHWxeRa20zvJeN8W91noNrJDmDyO&noverify=0&group_code=827977720" + # 如果kv值为空,那只能不传,空值似乎最多只允许一个 md = MessageMarkdown.model_validate({ @@ -41,9 +44,86 @@ def last_md(user_id, image_size: tuple, url: str) -> QQ_Msg: # ] # }) + # keyboard = MessageKeyboard.model_validate({ + # "id": "102083290_1707209565" + # }) + keyboard = MessageKeyboard.model_validate({ - "id": "102083290_1707209565" + "content": { + "rows": [{"buttons": [ + { + "id": "1", + "render_data": { + "label": f"{button_show}", + "visited_label": f"{button_show}", + "style": 0 + }, + "action": { + "type": 2, + "permission": { + "type": 2, + }, + "unsupport_tips": "客户端不支持", + "data": f"{button_cmd}", + } + } + + ]}, + {"buttons": [ + { + "id": "1", + "render_data": { + "label": f"{button_show2}", + "visited_label": f"{button_show2}", + "style": 0 + }, + "action": { + "type": 2, + "permission": { + "type": 2, + }, + "unsupport_tips": "客户端不支持", + "data": f"{button_cmd2}", + } + }, + { + "id": "1", + "render_data": { + "label": f"{button_show3}", + "visited_label": f"{button_show3}", + "style": 0 + }, + "action": { + "type": 2, + "permission": { + "type": 2, + }, + "unsupport_tips": "客户端不支持", + "data": f"{button_cmd3}", + } + }, + { + "id": "1", + "render_data": { + "label": f"{button_show4}", + "visited_label": f"{button_show4}", + "style": 0 + }, + "action": { + "type": 0, + "permission": { + "type": 2, + }, + "unsupport_tips": "客户端不支持", + "data": f"{button_cmd4}", + } + } + + ]} + ] + } }) + qq_msg = QQ_Msg([QQ_MsgSeg.markdown(md), QQ_MsgSeg.keyboard(keyboard)]) return qq_msg From aaa18b500f6f0eec831cd9369c6db84f8bc3e3ba Mon Sep 17 00:00:00 2001 From: cypas Date: Sat, 6 Jul 2024 21:51:49 +0800 Subject: [PATCH 02/17] =?UTF-8?q?perf:=E4=BC=98=E5=8C=96report=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E6=97=A0=E6=95=B0=E6=8D=AE=E6=97=B6=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/handle/report.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/handle/report.py b/nonebot_plugin_splatoon3_nso/handle/report.py index bfb9128..583c851 100644 --- a/nonebot_plugin_splatoon3_nso/handle/report.py +++ b/nonebot_plugin_splatoon3_nso/handle/report.py @@ -25,7 +25,7 @@ async def report(bot: Bot, event: Event, args: Message = CommandArg()): msg = get_report(platform, user_id, report_day=report_day) if not msg: if not report_day: - msg = f"```\n数据准备中,请明天再查询\n```" + msg = f"```\n数据准备中,在登陆bot两天后才可获取日报对比数据\n```" elif report_day: msg = f"```\n没有查询到所指定日期的日报数据```" @@ -144,8 +144,8 @@ async def report_all(bot: Bot, event: Event): def get_report_all_md(player_code): res = model_get_report_all(player_code) - if not res: - return "数据准备中" + if not res or len(res) == 1: + return "数据准备中,在登陆bot两天后才可获取全部日报对比数据" text = '' for r in res[:30]: _d = r From aaae4aacf845ff47f6bef4ba7773b810e1953f27 Mon Sep 17 00:00:00 2001 From: cypas Date: Tue, 9 Jul 2024 16:57:00 +0800 Subject: [PATCH 03/17] =?UTF-8?q?perf:BX=E5=8F=96=E6=B6=88=E9=80=9A?= =?UTF-8?q?=E7=94=A8=E7=BB=BFx=E4=BC=B0=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/handle/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nonebot_plugin_splatoon3_nso/handle/utils.py b/nonebot_plugin_splatoon3_nso/handle/utils.py index 402f525..b169021 100644 --- a/nonebot_plugin_splatoon3_nso/handle/utils.py +++ b/nonebot_plugin_splatoon3_nso/handle/utils.py @@ -29,7 +29,7 @@ # 徽章排名 dict_badges_ranking = { - "QmFkZ2UtMzEwMTAwMA==": "j3000", # 绿X 3000名 仅按日服计算 Badge-3101000 + # "QmFkZ2UtMzEwMTAwMA==": "j3000", # 绿X 3000名 仅按日服计算 Badge-3101000 # "QmFkZ2UtMzEwMTAwMQ==": "j500", # 银X 500名 Badge-3101001 # "QmFkZ2UtMzEwMTAwMg==": "j10", # 金X 10名 Badge-3101002 "QmFkZ2UtMzEwMTEwMA==": "j2000+", # 日服 2000+章 Badge-3101100 From f84c066cb60d2bd9956b03031b406a2f39ab779c Mon Sep 17 00:00:00 2001 From: cypas Date: Tue, 9 Jul 2024 23:55:19 +0800 Subject: [PATCH 04/17] =?UTF-8?q?feat:/last=20qq=E5=8D=A1=E7=89=87?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=85=AC=E5=91=8A=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/config.py | 2 +- nonebot_plugin_splatoon3_nso/data/utils.py | 21 ++++++++++++++++ nonebot_plugin_splatoon3_nso/handle/admin.py | 16 +++++++++++++ nonebot_plugin_splatoon3_nso/handle/last.py | 3 +++ nonebot_plugin_splatoon3_nso/handle/qq_md.py | 25 +++++++++++++------- nonebot_plugin_splatoon3_nso/utils/utils.py | 2 +- pyproject.toml | 3 ++- 7 files changed, 60 insertions(+), 12 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/config.py b/nonebot_plugin_splatoon3_nso/config.py index 4ec85b9..a250e34 100644 --- a/nonebot_plugin_splatoon3_nso/config.py +++ b/nonebot_plugin_splatoon3_nso/config.py @@ -48,4 +48,4 @@ class Config(BaseModel): driver = get_driver() global_config = driver.config -plugin_config = get_plugin_config(Config) +plugin_config = get_plugin_config(Config) \ No newline at end of file diff --git a/nonebot_plugin_splatoon3_nso/data/utils.py b/nonebot_plugin_splatoon3_nso/data/utils.py index c009d6e..8e75726 100644 --- a/nonebot_plugin_splatoon3_nso/data/utils.py +++ b/nonebot_plugin_splatoon3_nso/data/utils.py @@ -5,6 +5,9 @@ from .db_sqlite import DBSession, TempImageTable, DIR_TEMP_IMAGE from ..utils import init_path, get_file_url +# 插件数据全部变量提供静态读写 +plugin_data = {} + class GlobalUserInfo: """全局公用用户类""" @@ -100,3 +103,21 @@ def get_insert_or_update_obj(cls, filter_dict, **kw): setattr(res, k, v) session.close() return res + + +async def get_or_set_plugin_data(key, value=None): + """获取或设置插件数据""" + from nonebot import require + require("nonebot_plugin_datastore") + from nonebot_plugin_datastore import get_plugin_data + + global plugin_data + if not value: + # 读取配置 + value = await get_plugin_data().config.get(key) + plugin_data[key] = value + else: + # 存储配置 + await get_plugin_data().config.set(key, value) + plugin_data[key] = value + return value diff --git a/nonebot_plugin_splatoon3_nso/handle/admin.py b/nonebot_plugin_splatoon3_nso/handle/admin.py index d669b76..3acca41 100644 --- a/nonebot_plugin_splatoon3_nso/handle/admin.py +++ b/nonebot_plugin_splatoon3_nso/handle/admin.py @@ -10,6 +10,7 @@ from .send_msg import bot_send, notify_to_private from ..data.data_source import dict_get_all_global_users, model_clean_db_cache, model_get_or_set_user, \ dict_get_or_set_user_info +from ..data.utils import get_or_set_plugin_data from ..utils import get_msg_id from ..utils.bot import * from nonebot import logger @@ -125,6 +126,7 @@ async def admin_cmd(bot: Bot, event: Event, args: Message = CommandArg()): msg = "所有命令都需要加上/admin 前缀\n" \ "get_push 获取当前push统计\n" \ "close_push 关闭当前全部push\n" \ + "set_bot_notice {公告消息} 设置公告消息\n" \ "get_x_player 获取x赛top\n" \ "get_event_top 获取活动top\n" \ "clean_cache 清理数据库缓存\n" \ @@ -154,6 +156,20 @@ async def admin_cmd(bot: Bot, event: Event, args: Message = CommandArg()): else: await bot_send(bot, event, message=f"无效命令, lens:{len(args)}") + if plain_text.startswith("set_bot_notice"): + """设置公告消息 + set_bot_notice {notice} + """ + notice = plain_text.replace("set_bot_notice", "").strip().replace("\n", "\r") + old_notice = await get_or_set_plugin_data("splatoon3_bot_notice") + await get_or_set_plugin_data("splatoon3_bot_notice", notice) + if not old_notice: + old_notice = "None" + if not notice: + notice = "None" + msg = "旧公告消息为:\n" + old_notice + "\n" + "新的公告消息为:\n" + notice + await bot_send(bot, event, message=msg) + if plain_text.startswith("copy_token"): """复制同平台其他用户token到自己账号,方便测试""" args = plain_text.split(" ") diff --git a/nonebot_plugin_splatoon3_nso/handle/last.py b/nonebot_plugin_splatoon3_nso/handle/last.py index da513f1..850b993 100644 --- a/nonebot_plugin_splatoon3_nso/handle/last.py +++ b/nonebot_plugin_splatoon3_nso/handle/last.py @@ -6,6 +6,7 @@ from .utils import _check_session_handler, get_game_sp_id_and_name, get_battle_time_or_coop_time, get_event_info from .. import plugin_config from ..data.data_source import dict_get_or_set_user_info +from ..data.utils import get_or_set_plugin_data from ..s3s.splatnet_image import get_app_screenshot from ..s3s.splatoon import Splatoon from ..s3s.utils import SPLATNET3_URL @@ -19,6 +20,8 @@ async def last(bot: Bot, event: Event, args: Message = CommandArg()): """获取上一局对战或打工数据图""" platform = bot.adapter.get_name() user_id = event.get_user_id() + # 重载插件配置项 + await get_or_set_plugin_data("splatoon3_bot_notice") get_battle = False get_coop = False diff --git a/nonebot_plugin_splatoon3_nso/handle/qq_md.py b/nonebot_plugin_splatoon3_nso/handle/qq_md.py index 46ec812..aa7b6dd 100644 --- a/nonebot_plugin_splatoon3_nso/handle/qq_md.py +++ b/nonebot_plugin_splatoon3_nso/handle/qq_md.py @@ -1,6 +1,9 @@ +import re + from nonebot.adapters.qq.models import MessageKeyboard, MessageMarkdown from ..config import plugin_config +from ..data.utils import plugin_data from ..utils.bot import * @@ -9,14 +12,15 @@ def last_md(user_id, image_size: tuple, url: str) -> QQ_Msg: template_id = "102083290_1705920931" image_width, image_height = image_size text_start = "发送/nso帮助查看详细用法" - text_end = "自己手动/last也算是一种push推送吧" - button_show = "/last" + # text_end作为公告消息 + text_end = plugin_data.get("splatoon3_bot_notice") + button_show = "查对战或打工" button_cmd = "/last" - button_show2 = "/last b" + button_show2 = "查对战" button_cmd2 = "/last b" - button_show3 = "/last c" + button_show3 = "查打工" button_cmd3 = "/last c" button_show4 = "bot官方群" @@ -24,13 +28,16 @@ def last_md(user_id, image_size: tuple, url: str) -> QQ_Msg: # 如果kv值为空,那只能不传,空值似乎最多只允许一个 + params = [{"key": "at_user_id", "values": [f"<@{user_id}>"]}, + {"key": "text_start", "values": [f"{text_start}"]}, + {"key": "img_size", "values": [f"img#{image_width}px #{image_height}px"]}, + {"key": "img_url", "values": [f"{url}"]}] + if text_end: + text_end = text_end.replace("\\n", "\r").replace("\\r", "\r") + params.append({"key": "text_end", "values": [f"{text_end}"]}) md = MessageMarkdown.model_validate({ "custom_template_id": f"{template_id}", - "params": [{"key": "at_user_id", "values": [f"<@{user_id}>"]}, - {"key": "text_start", "values": [f"{text_start}"]}, - {"key": "img_size", "values": [f"img#{image_width}px #{image_height}px"]}, - {"key": "img_url", "values": [f"{url}"]}, - ] + "params": params }) # 完整kv对 diff --git a/nonebot_plugin_splatoon3_nso/utils/utils.py b/nonebot_plugin_splatoon3_nso/utils/utils.py index 6a75d3f..7f75cf5 100644 --- a/nonebot_plugin_splatoon3_nso/utils/utils.py +++ b/nonebot_plugin_splatoon3_nso/utils/utils.py @@ -1,6 +1,6 @@ import os -BOT_VERSION = "2.7.7" +BOT_VERSION = "2.7.8" DIR_RESOURCE = f"{os.path.abspath(os.path.join(__file__, os.pardir, os.pardir))}/resource" plugin_release_time = "2024-06-24 04:35:58" # 预留 2.0.0重构版nso插件发布时间,预计发布时对全部用户先显示一周,之后再判断用户创建时间 diff --git a/pyproject.toml b/pyproject.toml index 3982841..59d43fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nonebot-plugin-splatoon3-nso" -version = "1.5.7" +version = "1.5.8" description = "一个基于nonebot2框架的splatoon3游戏nso数据查询插件" authors = ["cypas "] readme = "README.md" @@ -18,6 +18,7 @@ nonebot-adapter-kaiheila = "^0.3.1" nonebot-adapter-qq = "^1.4.1" nonebot-plugin-apscheduler = "^0.4.0" nonebot_plugin_htmlrender = "^0.3.0" +nonebot_plugin_datastore = "^1.3.0" SQLAlchemy = "^2.0.25" playwright = "^1.40.0" Pillow="9.5.0" From 7637c831930c23b75d76dcb0e09d10700a61f617 Mon Sep 17 00:00:00 2001 From: cypas Date: Sat, 13 Jul 2024 02:25:25 +0800 Subject: [PATCH 05/17] =?UTF-8?q?feat:nso=E7=9B=B8=E5=85=B3=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E9=80=82=E9=85=8Dc2c=E7=A7=81=E8=81=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/__init__.py | 14 ++- nonebot_plugin_splatoon3_nso/data/utils.py | 3 +- .../handle/cron/stat_ink.py | 2 +- nonebot_plugin_splatoon3_nso/handle/last.py | 4 +- nonebot_plugin_splatoon3_nso/handle/login.py | 93 +++++++++++++------ nonebot_plugin_splatoon3_nso/handle/qq_md.py | 68 ++++++++++++-- .../handle/send_msg.py | 24 ++++- nonebot_plugin_splatoon3_nso/handle/utils.py | 2 +- nonebot_plugin_splatoon3_nso/s3s/iksm.py | 15 +-- nonebot_plugin_splatoon3_nso/utils/bot.py | 2 +- pyproject.toml | 2 +- 11 files changed, 175 insertions(+), 54 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/__init__.py b/nonebot_plugin_splatoon3_nso/__init__.py index 37271db..26a3ec0 100644 --- a/nonebot_plugin_splatoon3_nso/__init__.py +++ b/nonebot_plugin_splatoon3_nso/__init__.py @@ -1,5 +1,6 @@ from nonebot.message import event_preprocessor from nonebot.plugin import PluginMetadata +from nonebot.rule import is_type from .config import driver, plugin_config, Config from .data.db_sqlite import init_db @@ -24,8 +25,8 @@ ) -@on_startswith(("/", "、"), priority=99).handle() -async def unknown_command(bot: Bot, event: Event): +@on_startswith(("/", "、"), priority=99, block=True).handle() +async def unknown_command(bot: Bot, event: Event, matcher: Matcher): logger.info(f'unknown_command from {event.get_event_name()}') msg = "" if plugin_config.splatoon3_unknown_command_fallback_reply: @@ -44,6 +45,15 @@ async def unknown_command(bot: Bot, event: Event): logger.info("kook指定兜底黑名单服务器,不进行兜底消息提示") if msg: await bot.send(event, message=msg) + await matcher.finish() + + +@on_message(rule=is_type(QQ_C2CME), priority=98, block=True).handle() +async def c2c_unknown_command(bot: Bot, event: Event, matcher: Matcher): + """为qq c2c任何未匹配文本进行兜底""" + logger.info(f'unknown_command from {event.get_event_name()}') + msg = "无效指令,发送 /help 查看帮助" + await matcher.finish(msg) @on_command("help", aliases={"h", "帮助", "说明", "文档"}, priority=10).handle() diff --git a/nonebot_plugin_splatoon3_nso/data/utils.py b/nonebot_plugin_splatoon3_nso/data/utils.py index 8e75726..21063aa 100644 --- a/nonebot_plugin_splatoon3_nso/data/utils.py +++ b/nonebot_plugin_splatoon3_nso/data/utils.py @@ -55,8 +55,7 @@ async def model_get_or_set_temp_image(_type, name: str, link=None) -> TempImageT image_data = await get_file_url(link) file_name = "" # 1024 bytes长度 = 1k - lens = len(image_data) - if lens > 200: + if image_data and len(image_data) > 200: # 创建文件夹 init_path(f"{DIR_TEMP_IMAGE}") init_path(f"{DIR_TEMP_IMAGE}/{_type}") diff --git a/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py b/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py index 8e20403..cda03a2 100644 --- a/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py +++ b/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py @@ -37,7 +37,7 @@ async def sync_stat_ink(): error_cnt = 0 else_error_cnt = 0 notice_error_cnt = 0 - _pool = 10 + _pool = 20 for i in range(0, len(db_users), _pool): pool_users_list = db_users[i:i + _pool] tasks = [sync_stat_ink_func(db_user) for db_user in pool_users_list] diff --git a/nonebot_plugin_splatoon3_nso/handle/last.py b/nonebot_plugin_splatoon3_nso/handle/last.py index 850b993..dfb25cc 100644 --- a/nonebot_plugin_splatoon3_nso/handle/last.py +++ b/nonebot_plugin_splatoon3_nso/handle/last.py @@ -74,7 +74,9 @@ async def last(bot: Bot, event: Event, args: Message = CommandArg()): idx=idx, get_screenshot=get_screenshot, mask=mask) - if isinstance(event, QQ_GME) and plugin_config.splatoon3_qq_md_mode and not get_image: + if isinstance(event, (QQ_GME, QQ_C2CME)) and plugin_config.splatoon3_qq_md_mode and not get_image: + if isinstance(event, QQ_C2CME): + user_id = "" # 这里存在 /last ss 的情况,msg值实际为bytes await bot_send_last_md(bot, event, msg, user_id, image_width=image_width) else: diff --git a/nonebot_plugin_splatoon3_nso/handle/login.py b/nonebot_plugin_splatoon3_nso/handle/login.py index f162f83..dcf4bdb 100644 --- a/nonebot_plugin_splatoon3_nso/handle/login.py +++ b/nonebot_plugin_splatoon3_nso/handle/login.py @@ -1,4 +1,5 @@ import asyncio +import copy import secrets import threading import time @@ -7,7 +8,7 @@ from .cron import update_s3si_ts from .cron.stat_ink import sync_stat_ink_func from .utils import _check_session_handler, get_event_info, get_game_sp_id -from .send_msg import bot_send, notify_to_channel, bot_send_login_md +from .send_msg import bot_send, notify_to_channel, bot_send_login_md, bot_send_url_md from ..config import plugin_config from ..data.data_source import dict_get_or_set_user_info, model_delete_user, global_user_info_dict, \ model_get_or_set_user @@ -32,14 +33,18 @@ async def login_in(bot: Bot, event: Event, matcher: Matcher): # 只有q平台 且 q群才发md if isinstance(bot, QQ_Bot): if isinstance(event, QQ_GME) and plugin_config.splatoon3_qq_md_mode: + if isinstance(event, QQ_C2CME): + user_id = "" # 发送md await bot_send_login_md(bot, event, user_id) - else: + await matcher.finish() + elif isinstance(event, QQ_C2CME): + pass + elif not isinstance(event, (QQ_GME, QQ_C2CME)): msg = "QQ平台当前无法完成nso登录流程,请至其他平台完成登录后使用/getlc命令获取绑定码\n" \ f"Kook服务器id:{plugin_config.splatoon3_kk_guild_id}" await bot_send(bot, event, msg) - - await matcher.finish() + await matcher.finish() if isinstance(event, All_Group_Message): await matcher.finish(MSG_PRIVATE) @@ -82,14 +87,17 @@ async def login_in(bot: Bot, event: Event, matcher: Matcher): await bot.send(event, message=msg) elif isinstance(bot, All_BOT): - msg = "风险告知:小鱿鱿所使用的nso查询本质上为第三方nso软件,此类第三方调用可能会导致nso鱿鱼圈被封禁,目前未观察到游戏连带被禁的情况。(要怪请去怪乌贼研究所)\n" \ + msg = "风险告知:小鱿鱿所使用的nso查询本质上为第三方nso软件,此类第三方调用可能会导致nso鱿鱼圈被封禁一个月,目前未观察到游戏连带被禁的情况。(要怪请去怪乌贼研究所)\n" \ "若继续完成以下登录流程,则视为您已知晓此风险并继续使用nso查询\n\n" msg += "登录流程: 在浏览器中打开下面链接(移动端复制链接至其他浏览器,\n" \ "登陆后,在显示红色的选择此人按钮时,右键红色按钮(手机端长按复制)\n" \ "复制其链接后发送给机器人,链接是一串npf开头的文本(两分钟内有效!)" await bot.send(event, message=msg) await bot.send(event, message='我是分割线'.center(20, '-')) - await bot.send(event, message=url) + if isinstance(event, QQ_C2CME): + await bot_send_url_md(bot, event, "点我打开nso登录网页", url) + else: + await bot.send(event, message=url) matcher_login_in_2 = on_startswith("npf", priority=10) @@ -97,20 +105,24 @@ async def login_in(bot: Bot, event: Event, matcher: Matcher): @matcher_login_in_2.handle() async def login_in_2(bot: Bot, event: Event): - text = event.get_plaintext() + text = event.get_plaintext().strip().replace("<", "<").replace(">", ">").replace("&", "&") platform = bot.adapter.get_name() user_id = event.get_user_id() msg_id = get_msg_id(platform, user_id) # 查找用户登录字典 user_login_status = global_login_status_dict.get(msg_id) if user_login_status is None: + await bot.send(event, message="请重新发送 /login 使用新地址登录后,重新发送按钮的新链接") return auth_code_verifier = user_login_status.get("auth_code_verifier") s3s: S3S = user_login_status.get("s3s") - err_msg = "登录失败,请 /login 重试, 并在浏览器打开bot新发给你的登录链接,在重新完成登录后,复制按钮的新链接给我,链接是一串npf开头的文本" - if (not text) or (len(text) < 500) or (not text.startswith('npf')) or (auth_code_verifier is None): + if not auth_code_verifier: + await bot.send(event, message="请重新发送 /login 使用新地址登录后,重新发送按钮的新链接") + return + if (not text) or (len(text) < 500) or (not text.startswith('npf')): + err_msg = "登录链接格式错误,链接是一串npf开头的文本" logger.info(err_msg) # 登录失败直接销毁用户等待字典 global_login_status_dict.pop(msg_id) @@ -118,8 +130,11 @@ async def login_in_2(bot: Bot, event: Event): return session_token = await s3s.login_in_2(use_account_url=text, auth_code_verifier=auth_code_verifier) - if not session_token or session_token == 'skip': + if (not session_token) or (session_token == 'skip'): + err_msg = "登录失败,请 /login 重试, 并在浏览器打开bot新发给你的登录链接,在重新完成登录后,复制按钮的新链接给我" logger.info(err_msg) + # 登录失败直接销毁用户等待字典 + global_login_status_dict.pop(msg_id) await bot.send(event, message=err_msg) return logger.info(f'session_token: {session_token}') @@ -142,7 +157,7 @@ async def login_in_2(bot: Bot, event: Event): "/set_stat_key - set stat.ink api_key, bot will sync your data to stat.ink" elif isinstance(bot, All_BOT): msg = "登录成功!机器人现在可以从App获取你的数据。\n" \ - "如果希望在q群使用nso查询,请发送\n" \ + "如果希望在其他平台使用nso查询,请发送\n" \ "/get_login_code\n" \ "获取一次性跨平台绑定码\n" \ "\n" \ @@ -154,6 +169,19 @@ async def login_in_2(bot: Bot, event: Event): "/start_push - 开启推送模式\n" \ "/set_stat_key - 设置 api_key, 同步数据到 https://stat.ink\n" \ "更多完整nso操作指令:\n" + if isinstance(event, QQ_C2CME): + msg = "登录成功!机器人现在可以从App获取你的数据。\n" \ + "如果希望在其他平台使用nso查询,请发送\n" \ + "/get_login_code\n" \ + "获取一次性跨平台绑定码\n" \ + "\n" \ + "常用指令:\n" \ + "/me - 显示你的信息\n" \ + "/friends - 显示在线的喷喷好友\n" \ + "/last - 显示最近一场对战或打工\n" \ + "/report - 获取昨天或指定日期的日报数据\n" \ + "/set_stat_key - 设置 api_key, 同步数据到 https://stat点ink\n" \ + "更多完整nso操作指令:\n" if plugin_config.splatoon3_schedule_plugin_priority_mode: # 日程插件帮助优先模式 @@ -182,28 +210,36 @@ async def login_in_2(bot: Bot, event: Event): async def clear_db_info(bot: Bot, event: Event): """清空账号数据""" platform = bot.adapter.get_name() - if isinstance(event, All_Group_Message_Without_QQ_G): - await bot_send(bot, event, "请私聊机器人") + if isinstance(event, All_Group_Message): + await bot_send(bot, event, MSG_PRIVATE) return user_id = event.get_user_id() msg_id = get_msg_id(platform, user_id) + + user = dict_get_or_set_user_info(platform, user_id) + log_msg = "用户注销:db_id:{},msg_id:{},会话昵称:{},游戏昵称:{}".format( + user.db_id, msg_id, user.user_name, user.game_name) + notify_msg = "用户注销:db_id:{},msg_id:{},\n会话昵称:{},游戏昵称:{}".format( + user.db_id, msg_id, user.user_name, user.game_name) model_delete_user(platform, user_id) if msg_id in global_user_info_dict: global_user_info_dict.pop(msg_id) - msg = "All your data cleared! 已清空账号数据!" - logger.info(msg) + if isinstance(bot, Tg_Bot): + msg = "All your data cleared!" + else: + msg = "已清空账号数据!" + logger.info(log_msg) + await bot_send(bot, event, message=msg) + await notify_to_channel(notify_msg) @on_command("get_login_code", aliases={'getlogincode', 'glc', 'getlc'}, priority=10, block=True).handle( parameterless=[Depends(_check_session_handler)]) async def get_login_code(bot: Bot, event: Event): """获取绑定码""" - if isinstance(event, QQ_GME): - await bot_send(bot, event, "q群暂不支持此功能") - return if isinstance(event, All_Group_Message): await bot_send(bot, event, MSG_PRIVATE) return @@ -284,14 +320,15 @@ async def set_login_code(bot: Bot, event: Event): @matcher_set_api_key.handle(parameterless=[Depends(_check_session_handler)]) async def set_api_key(bot: Bot, event: Event): """设置stat.ink的api_key""" - if isinstance(bot, QQ_Bot): - await bot_send(bot, event, "QQ平台暂不支持该命令,请从其他平台进行设置") - return if isinstance(event, All_Group_Message): await matcher_set_api_key.finish(MSG_PRIVATE) return if isinstance(bot, Tg_Bot): msg = "Please copy you api_key from https://stat.ink/profile then paste below" + elif isinstance(bot, QQ_Bot): + msg = "请从 https://stat点ink/profile (自行替换 点)页面复制你的 api_key 后,将key直接发送给机器人\n" \ + "注册stat点ink账号后,无需其他操作,设置api_key后,\n" \ + "机器人会同步你的数据到 stat点ink (App最多保存最近50*5场对战和50场打工数据,该网站可记录全部对战或打工,也可用于武器/地图/模式/胜率的战绩分析)" elif isinstance(bot, All_BOT): msg = "请从 https://stat.ink/profile 页面复制你的 api_key 后,将key直接发送给机器人\n" \ "注册stat.ink账号后,无需其他操作,设置api_key后,\n" \ @@ -304,7 +341,7 @@ async def get_set_api_key(bot: Bot, event: Event): """stat api key匹配""" if isinstance(event, All_Group_Message): return - stat_key = event.get_plaintext().strip() + stat_key = event.get_plaintext().strip().replace("<", "<").replace(">", ">").replace("&", "&") if len(stat_key) != 43: await matcher_set_api_key.finish("key错误,请重新复制key后发送给我") return @@ -318,8 +355,11 @@ async def get_set_api_key(bot: Bot, event: Event): if isinstance(bot, Tg_Bot): msg = "set_api_key success, bot will check every 2 hours and post your data to stat.ink.\n" \ "first sync will be in minutes." + elif isinstance(bot, QQ_Bot): + msg = "设置成功,bot将开始同步你当前的对战及打工数据到 stat点ink,并后续每2h自动进行一次同步\n" \ + "因QQ平台主动推送限制,同步成功时Bot无法主动推送消息,如需确认,请在三分钟后前往stat网站自行查看记录,kook平台bot才可以主动推送" elif isinstance(bot, All_BOT): - msg = f"设置成功,bot将开始同步你当前的对战及打工数据到 stat.ink,并后续每2h进行一次同步" + msg = f"设置成功,bot将开始同步你当前的对战及打工数据到 stat.ink,并后续每2h自动进行一次同步" await bot_send(bot, event, message=msg) await update_s3si_ts() @@ -329,15 +369,14 @@ async def get_set_api_key(bot: Bot, event: Event): @on_command("sync_now", priority=10, block=True).handle(parameterless=[Depends(_check_session_handler)]) async def sync_now(bot: Bot, event: Event): - if isinstance(bot, QQ_Bot): - await bot_send(bot, event, "QQ平台暂不支持该命令,请在其他平台使用该命令") - return platform = bot.adapter.get_name() user_id = event.get_user_id() user = dict_get_or_set_user_info(platform, user_id) if not (user and user.session_token and user.stat_key): if isinstance(bot, Tg_Bot): msg = "Please set api_key first, /set_stat_key" + elif isinstance(bot, QQ_Bot): + msg = "请先设置 stat点ink网站的api_key, 指令:/set_stat_key" elif isinstance(bot, All_BOT): msg = "请先设置 stat.ink网站的api_key, 指令:/set_stat_key" await bot_send(bot, event, msg) @@ -345,6 +384,8 @@ async def sync_now(bot: Bot, event: Event): await update_s3si_ts() msg = "战绩手动同步任务已开始,请稍等..." + if isinstance(bot, QQ_Bot): + msg += "\n因QQ平台主动推送限制,同步成功时Bot无法主动推送消息,如需确认,请在三分钟后前往stat点ink网站自行查看记录,kook平台bot才可以主动推送" db_user = model_get_or_set_user(platform, user_id) if db_user: await bot_send(bot, event, msg) diff --git a/nonebot_plugin_splatoon3_nso/handle/qq_md.py b/nonebot_plugin_splatoon3_nso/handle/qq_md.py index aa7b6dd..55ec9a1 100644 --- a/nonebot_plugin_splatoon3_nso/handle/qq_md.py +++ b/nonebot_plugin_splatoon3_nso/handle/qq_md.py @@ -27,11 +27,12 @@ def last_md(user_id, image_size: tuple, url: str) -> QQ_Msg: button_cmd4 = "http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=zGefDQ4GQYFPAB-hFkeFLlyQ8qbG5S2w&authKey=j0b9yXmtSzYry6qQQ%2FFXxw7U%2Fp6kXyET0xj%2BRHWxeRa20zvJeN8W91noNrJDmDyO&noverify=0&group_code=827977720" # 如果kv值为空,那只能不传,空值似乎最多只允许一个 - - params = [{"key": "at_user_id", "values": [f"<@{user_id}>"]}, - {"key": "text_start", "values": [f"{text_start}"]}, - {"key": "img_size", "values": [f"img#{image_width}px #{image_height}px"]}, - {"key": "img_url", "values": [f"{url}"]}] + params = [] + if user_id: + params.append({"key": "at_user_id", "values": [f"<@{user_id}>"]}) + params.extend([{"key": "text_start", "values": [f"{text_start}"]}, + {"key": "img_size", "values": [f"img#{image_width}px #{image_height}px"]}, + {"key": "img_url", "values": [f"{url}"]}]) if text_end: text_end = text_end.replace("\\n", "\r").replace("\\r", "\r") params.append({"key": "text_end", "values": [f"{text_end}"]}) @@ -136,6 +137,7 @@ def last_md(user_id, image_size: tuple, url: str) -> QQ_Msg: def login_md(user_id, check_session=False) -> QQ_Msg: + """无法使用login时转kook登录的md卡片提示""" template_id = "102083290_1705923685" data1 = "" if check_session: @@ -145,12 +147,18 @@ def login_md(user_id, check_session=False) -> QQ_Msg: button_show = "kook服务器" kook_jump_link = "https://www.kookapp.cn/app/invite/mkjIOn" + params = [] + if user_id: + params.append({"key": "title", "values": [f"<@{user_id}>"]}) + else: + params.append({"key": "title", "values": [f"当前平台无法登录"]}) + params.extend([{"key": "data1", "values": [f"{data1}"]}, + {"key": "data2", "values": [f"{data2}"]}, + ]) + md = MessageMarkdown.model_validate({ "custom_template_id": f"{template_id}", - "params": [{"key": "title", "values": [f"<@{user_id}>"]}, - {"key": "data1", "values": [f"{data1}"]}, - {"key": "data2", "values": [f"{data2}"]}, - ] + "params": params }) keyboard = MessageKeyboard.model_validate({ @@ -179,3 +187,45 @@ def login_md(user_id, check_session=False) -> QQ_Msg: }) qq_msg = QQ_Msg([QQ_MsgSeg.markdown(md), QQ_MsgSeg.keyboard(keyboard)]) return qq_msg + + +def url_md(title, content, url_title, url) -> QQ_Msg: + """仅发一个url的按钮卡片""" + template_id = "102083290_1705923685" + if not title: + title = " " + if not content: + content = "\r" + params = [{"key": "title", "values": [f"{title}"]}] + params.extend([{"key": "data1", "values": [f"{content}"]}]) + + md = MessageMarkdown.model_validate({ + "custom_template_id": f"{template_id}", + "params": params + }) + keyboard = MessageKeyboard.model_validate({ + "content": { + "rows": [{"buttons": [ + { + "id": "1", + "render_data": { + "label": f"{url_title}", + "visited_label": f"{url_title}", + "style": 0 + }, + "action": { + "type": 0, + "permission": { + "type": 2, + }, + "unsupport_tips": "客户端不支持", + "data": f"{url}", + } + } + + ]}, + ] + } + }) + qq_msg = QQ_Msg([QQ_MsgSeg.markdown(md), QQ_MsgSeg.keyboard(keyboard)]) + return qq_msg diff --git a/nonebot_plugin_splatoon3_nso/handle/send_msg.py b/nonebot_plugin_splatoon3_nso/handle/send_msg.py index 13e93bc..6ffb9ef 100644 --- a/nonebot_plugin_splatoon3_nso/handle/send_msg.py +++ b/nonebot_plugin_splatoon3_nso/handle/send_msg.py @@ -2,7 +2,7 @@ from PIL import Image -from .qq_md import last_md, login_md +from .qq_md import last_md, login_md, url_md from ..utils import DIR_RESOURCE, get_msg_id, get_time_now_china from ..utils.bot import * from ..config import plugin_config @@ -204,6 +204,22 @@ async def bot_send_login_md(bot: Bot, event: Event, user_id: str, check_session= await bot.send(event, qq_msg) +async def bot_send_url_md(bot: Bot, event: Event, url_title, url): + """发送url md消息""" + title = "nso登录" + content = "详细nso登录步骤可点击bot头像,查看文档内的教程" + qq_msg = url_md(title, content, url_title, url) + await bot.send(event, qq_msg) + + +async def bot_send_login_url_md(bot: Bot, event: Event, url_title, url): + """发送url md消息""" + title = "" + content = "" + qq_msg = url_md(title, content, url_title, url) + await bot.send(event, qq_msg) + + async def send_msg(bot: Bot, event: Event, msg: str | bytes): """公用send_msg""" # 指定回复模式 @@ -258,12 +274,12 @@ async def send_msg(bot: Bot, event: Event, msg: str | bytes): await bot.send(event, Kook_MsgSeg.image(url), reply_sender=reply_mode) elif isinstance(bot, QQ_Bot): try: - if not isinstance(event, GroupAtMessageCreateEvent): - await bot.send(event, message=QQ_MsgSeg.file_image(img)) - else: + if isinstance(event, (QQ_GME, QQ_C2CME)): url = await get_image_url(img) if url: await bot.send(event, message=QQ_MsgSeg.image(url)) + else: + await bot.send(event, message=QQ_MsgSeg.file_image(img)) except QQ_ActionFailed as e: if "消息被去重" in str(e): pass diff --git a/nonebot_plugin_splatoon3_nso/handle/utils.py b/nonebot_plugin_splatoon3_nso/handle/utils.py index b169021..a945176 100644 --- a/nonebot_plugin_splatoon3_nso/handle/utils.py +++ b/nonebot_plugin_splatoon3_nso/handle/utils.py @@ -277,7 +277,7 @@ async def get_event_info(bot, event): elif isinstance(event, QQ_C2CME): # c2c私信 data.update({ - 'user_name': 'C2C', + 'user_name': 'QQ私信', }) # if 'group' in event.get_event_name(): # qq 都在群里使用 diff --git a/nonebot_plugin_splatoon3_nso/s3s/iksm.py b/nonebot_plugin_splatoon3_nso/s3s/iksm.py index 25656ba..9265b12 100644 --- a/nonebot_plugin_splatoon3_nso/s3s/iksm.py +++ b/nonebot_plugin_splatoon3_nso/s3s/iksm.py @@ -22,7 +22,7 @@ NSOAPP_VERSION = "unknown" NSOAPP_VER_FALLBACK = "2.10.1" # fallback WEB_VIEW_VERSION = "unknown" -WEB_VIEW_VER_FALLBACK = "6.0.0-9f87c815" # fallback +WEB_VIEW_VER_FALLBACK = "6.0.0-f734f04c" # fallback F_GEN_URL = "https://api.imink.app/f" F_GEN_URL_2 = "https://nxapi-znca-api.fancy.org.uk/api/znca/f" @@ -200,18 +200,21 @@ async def login_in_2(self, use_account_url, auth_code_verifier): try: if use_account_url == "skip": return "skip" - session_token_code = re.search('de=(.*)&st', use_account_url).group(1) - session_token = await self.get_session_token(session_token_code, auth_code_verifier) + match = re.search("de=(.*)&st", use_account_url) + session_token_code = match.group(1) + resp = await self.get_session_token(session_token_code, auth_code_verifier) + session_token = resp["session_token"] return session_token except KeyboardInterrupt: print("\nBye!") return "skip" except AttributeError: print("Malformed URL. Please try again, or press Ctrl+C to exit.") - print("URL:", end=' ') return "skip" except KeyError: # session_token not found print("\nThe URL has expired. Please log out and back into your Nintendo Account and try again.") + print(f"get_session_token error,resp:{resp}") + print(f"get_session_token error,\nsession_token_code:{session_token_code},\nauth_code_verifier:{auth_code_verifier.replace(b'=', b'').decode('utf-8')},\nresp:{resp}") return "skip" except Exception as ex: print(f'ex: {ex}') @@ -240,10 +243,10 @@ async def get_session_token(self, session_token_code, auth_code_verifier): } url = 'https://accounts.nintendo.com/connect/1.0.0/api/session_token' - r = await self.req_client.post(url, headers=app_head, data=body) + session_token = json.loads(r.text) - return json.loads(r.text)["session_token"] + return session_token async def _get_id_token_and_user_info(self, session_token): """get_gtoken第一步""" diff --git a/nonebot_plugin_splatoon3_nso/utils/bot.py b/nonebot_plugin_splatoon3_nso/utils/bot.py index 8eb2649..07bc725 100644 --- a/nonebot_plugin_splatoon3_nso/utils/bot.py +++ b/nonebot_plugin_splatoon3_nso/utils/bot.py @@ -7,7 +7,7 @@ from nonebot.internal.params import Depends from nonebot.internal.adapter import Message from nonebot.params import CommandArg -from nonebot import get_bots, logger, on_regex, on_command, on_startswith, require +from nonebot import get_bots, logger, on_regex, on_command, on_startswith, require, on_message # onebot11 协议 from nonebot.adapters.onebot.v11 import Bot as V11_Bot diff --git a/pyproject.toml b/pyproject.toml index 59d43fc..06045c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ pydantic = ">=1.0.0,<3.0.0,!=2.5.0,!=2.5.1" nonebot-adapter-onebot = "^2.4.1" nonebot-adapter-telegram= "^0.1.0b16" nonebot-adapter-kaiheila = "^0.3.1" -nonebot-adapter-qq = "^1.4.1" +nonebot-adapter-qq = "^1.4.4" nonebot-plugin-apscheduler = "^0.4.0" nonebot_plugin_htmlrender = "^0.3.0" nonebot_plugin_datastore = "^1.3.0" From 31acff1decf2f8989d35cf139183aa086387f551 Mon Sep 17 00:00:00 2001 From: cypas Date: Sat, 13 Jul 2024 02:26:00 +0800 Subject: [PATCH 06/17] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E7=BD=91=E7=BB=9C?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E5=9B=BE=E7=89=87=E6=97=B6=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/utils/http.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/utils/http.py b/nonebot_plugin_splatoon3_nso/utils/http.py index 982b9af..17417c5 100644 --- a/nonebot_plugin_splatoon3_nso/utils/http.py +++ b/nonebot_plugin_splatoon3_nso/utils/http.py @@ -2,6 +2,7 @@ import httpx from httpx import Response +from nonebot import logger from .utils import get_msg_id from ..config import plugin_config @@ -19,10 +20,14 @@ async def get_file_url(url): """从网页读获取图片""" - resp = await AsHttpReq.get(url) - resp.read() - data = resp.content - return data + try: + resp = await AsHttpReq.get(url) + resp.read() + data = resp.content + return data + except Exception as e: + logger.warning(f"http get file_data error,{e}") + return None def get_or_init_client(platform, user_id, _type="normal", with_proxy=False): From 62317ad3eca8eb52d32f78b88d179251eb94819e Mon Sep 17 00:00:00 2001 From: cypas Date: Sat, 13 Jul 2024 02:52:11 +0800 Subject: [PATCH 07/17] =?UTF-8?q?perf:stat.ink=E5=90=8C=E6=AD=A5=E6=97=B6?= =?UTF-8?q?=E5=A4=8D=E7=94=A8global=5Fuser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handle/cron/stat_ink.py | 61 ++++++++++++++----- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py b/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py index cda03a2..78b3214 100644 --- a/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py +++ b/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py @@ -10,9 +10,11 @@ from ...data.db_sqlite import UserTable from ...config import plugin_config from ...s3s.iksm import F_GEN_URL_2, F_GEN_URL +from ...s3s.splatoon import Splatoon from ...utils import proxy_address, convert_td -from ...utils.utils import DIR_RESOURCE, init_path -from ...data.data_source import model_get_all_stat_user +from ...utils.utils import DIR_RESOURCE, init_path, get_msg_id +from ...data.data_source import model_get_all_stat_user, dict_clear_user_info_dict, global_user_info_dict, \ + dict_get_or_set_user_info from ..send_msg import notify_to_private, report_notify_to_channel, cron_notify_to_channel from .utils import user_remove_duplicates, cron_logger from ...utils.bot import Kook_ActionFailed @@ -56,12 +58,16 @@ async def sync_stat_ink(): else_error_cnt += 1 # 耗时 str_time = convert_td(dt.utcnow() - t) + # 清理任务字典 + cron_logger.info(f'clear cron user_info_dict...') + clear_count = await dict_clear_user_info_dict(_type="cron") cron_msg = (f"sync_stat_ink end: {str_time}\n" - f"complete_cnt: {complete_cnt}, upload_cnt: {upload_cnt}\n" - f"error_cnt: {error_cnt},notice_error_cnt: {notice_error_cnt},else_error_cnt: {else_error_cnt}") + f"complete_cnt: {complete_cnt}, upload_cnt: {upload_cnt}, clean_cnt: {clear_count}\n" + f"error_cnt: {error_cnt},notice_error_cnt: {notice_error_cnt}") cron_logger.info(cron_msg) - notice_msg = (f"耗时:{str_time}\n完成: {complete_cnt},同步: {upload_cnt}\n" - f"错误: {error_cnt},通知错误: {notice_error_cnt},配置错误: {else_error_cnt}") + notice_msg = (f"耗时:{str_time}\n完成: {complete_cnt},同步: {upload_cnt}, 清理临时对象数量: {clear_count}\n" + f"错误: {error_cnt},通知错误: {notice_error_cnt}") + await cron_notify_to_channel("sync_stat_ink", "end", notice_msg) @@ -71,7 +77,7 @@ async def sync_stat_ink_func(db_user: UserTable): cron_logger.debug(f"get user: {db_user.user_name}, have stat_key: {db_user.stat_key}") - res = get_post_stat_msg(db_user) + res = await get_post_stat_msg(db_user) if not isinstance(res, tuple): is_else_error = True return is_complete, is_upload, is_error, is_notice_error, is_else_error @@ -98,19 +104,46 @@ async def sync_stat_ink_func(db_user: UserTable): return is_complete, is_upload, is_error, is_notice_error, is_else_error -def get_post_stat_msg(db_user): +async def get_post_stat_msg(db_user): """获取同步消息文本""" - cron_logger.debug(f"get user: {db_user.user_name}, have stat_key: {db_user.stat_key}") if not (db_user and db_user.session_token and db_user.stat_key): return + # User复用以及定时任务用user对象 + platform = db_user.platform + user_id = db_user.user_id + msg_id = get_msg_id(platform, user_id) + global_user_info = global_user_info_dict.get(msg_id) + if global_user_info: + u = global_user_info + else: + # 新建cron任务对象 + u = dict_get_or_set_user_info(platform, user_id, _type="cron") + if not u or not u.session_token: + return + splatoon = Splatoon(None, None, u, _type="cron") + try: + # 刷新token + await splatoon.refresh_gtoken_and_bullettoken() + except ValueError as e: + if 'invalid_grant' in str(e) or 'Membership required' in str(e) or "has be banned" in str(e): + # 无效登录或会员过期 或被封禁 + # 关闭连接池 + await splatoon.req_client.close() + return "", str(e) + except Exception as e: + cron_logger.error(f'stat_ink_task error: {msg_id},refresh_gtoken_and_bullettoken error:{e}') + return "", str(e) + finally: + # 关闭连接池 + await splatoon.req_client.close() # 两个f_api 负载均衡 f_url_lst = [F_GEN_URL, F_GEN_URL_2] random.shuffle(f_url_lst) f_gen_url = f_url_lst[0] - res = exported_to_stat_ink(db_user.id, db_user.session_token, db_user.stat_key, f_gen_url, g_token=db_user.g_token, - bullet_token=db_user.bullet_token) + res = exported_to_stat_ink(db_user.id, u.session_token, db_user.stat_key, f_gen_url, g_token=u.g_token, + bullet_token=u.bullet_token) if not isinstance(res, tuple): return @@ -127,9 +160,9 @@ def get_post_stat_msg(db_user): next_f_str = "F_URL" next_f_url = F_GEN_URL cron_logger.warning(f"{db_user.id}, {db_user.user_name}, {now_f_str} Error,try {next_f_str} again") - res = exported_to_stat_ink(db_user.id, db_user.session_token, db_user.stat_key, next_f_url, - g_token=db_user.g_token, - bullet_token=db_user.bullet_token) + res = exported_to_stat_ink(db_user.id, u.session_token, db_user.stat_key, next_f_url, + g_token=u.g_token, + bullet_token=u.bullet_token) if not isinstance(res, tuple): return From a8bd68a3d8f78ad1cf9773d3cbfef83e1a27b77e Mon Sep 17 00:00:00 2001 From: cypas Date: Sat, 13 Jul 2024 14:33:53 +0800 Subject: [PATCH 08/17] =?UTF-8?q?feat:c2c=20login=20md=E5=8D=A1=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/handle/login.py | 6 +- nonebot_plugin_splatoon3_nso/handle/qq_md.py | 63 +++++++++++++++++++ .../handle/send_msg.py | 16 +---- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/handle/login.py b/nonebot_plugin_splatoon3_nso/handle/login.py index dcf4bdb..1b02ab2 100644 --- a/nonebot_plugin_splatoon3_nso/handle/login.py +++ b/nonebot_plugin_splatoon3_nso/handle/login.py @@ -8,7 +8,7 @@ from .cron import update_s3si_ts from .cron.stat_ink import sync_stat_ink_func from .utils import _check_session_handler, get_event_info, get_game_sp_id -from .send_msg import bot_send, notify_to_channel, bot_send_login_md, bot_send_url_md +from .send_msg import bot_send, notify_to_channel, bot_send_login_md, bot_send_login_url_md from ..config import plugin_config from ..data.data_source import dict_get_or_set_user_info, model_delete_user, global_user_info_dict, \ model_get_or_set_user @@ -95,7 +95,7 @@ async def login_in(bot: Bot, event: Event, matcher: Matcher): await bot.send(event, message=msg) await bot.send(event, message='我是分割线'.center(20, '-')) if isinstance(event, QQ_C2CME): - await bot_send_url_md(bot, event, "点我打开nso登录网页", url) + await bot_send_login_url_md(bot, event, url) else: await bot.send(event, message=url) @@ -367,7 +367,7 @@ async def get_set_api_key(bot: Bot, event: Event): threading.Thread(target=asyncio.run, args=(sync_stat_ink_func(db_user),)).start() -@on_command("sync_now", priority=10, block=True).handle(parameterless=[Depends(_check_session_handler)]) +@on_command("sync_now", aliases={'sync', 'syncnow', 'syncstat'}, priority=10, block=True).handle(parameterless=[Depends(_check_session_handler)]) async def sync_now(bot: Bot, event: Event): platform = bot.adapter.get_name() user_id = event.get_user_id() diff --git a/nonebot_plugin_splatoon3_nso/handle/qq_md.py b/nonebot_plugin_splatoon3_nso/handle/qq_md.py index 55ec9a1..a72c22f 100644 --- a/nonebot_plugin_splatoon3_nso/handle/qq_md.py +++ b/nonebot_plugin_splatoon3_nso/handle/qq_md.py @@ -189,6 +189,69 @@ def login_md(user_id, check_session=False) -> QQ_Msg: return qq_msg +def c2c_login_md(login_url) -> QQ_Msg: + """c2c login卡片""" + template_id = "102083290_1705923685" + docs_url = "https://docs.qq.com/doc/DSVlLSnloTGZqTmNz" + + title = "nso登录" + content = "详细nso登录步骤可查询下面文档教程\r!!!\r打开nso登录地址后不要用QQ内置浏览器,点右上角三个点,然后用系统浏览器打开\r!!!" + docs_url_title = "小鱿鱿使用文档及教程" + login_url_title = "点我打开nso登录网页" + params = [{"key": "title", "values": [f"{title}"]}] + params.extend([{"key": "data1", "values": [f"{content}"]}]) + + md = MessageMarkdown.model_validate({ + "custom_template_id": f"{template_id}", + "params": params + }) + keyboard = MessageKeyboard.model_validate({ + "content": { + "rows": [{"buttons": [ + { + "id": "1", + "render_data": { + "label": f"{docs_url_title}", + "visited_label": f"{docs_url_title}", + "style": 0 + }, + "action": { + "type": 0, + "permission": { + "type": 2, + }, + "unsupport_tips": "客户端不支持", + "data": f"{docs_url}", + } + } + + ]}, + {"buttons": [ + { + "id": "1", + "render_data": { + "label": f"{login_url_title}", + "visited_label": f"{login_url_title}", + "style": 0 + }, + "action": { + "type": 0, + "permission": { + "type": 2, + }, + "unsupport_tips": "客户端不支持", + "data": f"{login_url}", + } + } + + ]}, + ] + } + }) + qq_msg = QQ_Msg([QQ_MsgSeg.markdown(md), QQ_MsgSeg.keyboard(keyboard)]) + return qq_msg + + def url_md(title, content, url_title, url) -> QQ_Msg: """仅发一个url的按钮卡片""" template_id = "102083290_1705923685" diff --git a/nonebot_plugin_splatoon3_nso/handle/send_msg.py b/nonebot_plugin_splatoon3_nso/handle/send_msg.py index 6ffb9ef..fa70fb3 100644 --- a/nonebot_plugin_splatoon3_nso/handle/send_msg.py +++ b/nonebot_plugin_splatoon3_nso/handle/send_msg.py @@ -2,7 +2,7 @@ from PIL import Image -from .qq_md import last_md, login_md, url_md +from .qq_md import last_md, login_md, url_md, c2c_login_md from ..utils import DIR_RESOURCE, get_msg_id, get_time_now_china from ..utils.bot import * from ..config import plugin_config @@ -204,19 +204,9 @@ async def bot_send_login_md(bot: Bot, event: Event, user_id: str, check_session= await bot.send(event, qq_msg) -async def bot_send_url_md(bot: Bot, event: Event, url_title, url): +async def bot_send_login_url_md(bot: Bot, event: Event, url): """发送url md消息""" - title = "nso登录" - content = "详细nso登录步骤可点击bot头像,查看文档内的教程" - qq_msg = url_md(title, content, url_title, url) - await bot.send(event, qq_msg) - - -async def bot_send_login_url_md(bot: Bot, event: Event, url_title, url): - """发送url md消息""" - title = "" - content = "" - qq_msg = url_md(title, content, url_title, url) + qq_msg = c2c_login_md(url) await bot.send(event, qq_msg) From be70d2d0db7d10f9673dc02acc815d04f6d37647 Mon Sep 17 00:00:00 2001 From: cypas Date: Sat, 13 Jul 2024 14:34:27 +0800 Subject: [PATCH 09/17] =?UTF-8?q?feat:stat=E5=90=8C=E6=AD=A5=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E8=B5=B0=E5=85=A8=E5=B1=80=E5=85=AC=E5=85=B1user?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py b/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py index 78b3214..85a6d97 100644 --- a/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py +++ b/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py @@ -58,14 +58,11 @@ async def sync_stat_ink(): else_error_cnt += 1 # 耗时 str_time = convert_td(dt.utcnow() - t) - # 清理任务字典 - cron_logger.info(f'clear cron user_info_dict...') - clear_count = await dict_clear_user_info_dict(_type="cron") cron_msg = (f"sync_stat_ink end: {str_time}\n" - f"complete_cnt: {complete_cnt}, upload_cnt: {upload_cnt}, clean_cnt: {clear_count}\n" + f"complete_cnt: {complete_cnt}, upload_cnt: {upload_cnt}\n" f"error_cnt: {error_cnt},notice_error_cnt: {notice_error_cnt}") cron_logger.info(cron_msg) - notice_msg = (f"耗时:{str_time}\n完成: {complete_cnt},同步: {upload_cnt}, 清理临时对象数量: {clear_count}\n" + notice_msg = (f"耗时:{str_time}\n完成: {complete_cnt},同步: {upload_cnt}\n" f"错误: {error_cnt},通知错误: {notice_error_cnt}") await cron_notify_to_channel("sync_stat_ink", "end", notice_msg) @@ -118,10 +115,10 @@ async def get_post_stat_msg(db_user): u = global_user_info else: # 新建cron任务对象 - u = dict_get_or_set_user_info(platform, user_id, _type="cron") + u = dict_get_or_set_user_info(platform, user_id) if not u or not u.session_token: return - splatoon = Splatoon(None, None, u, _type="cron") + splatoon = Splatoon(None, None, u) try: # 刷新token await splatoon.refresh_gtoken_and_bullettoken() From 757c376fef8393e155845e14da05deb2b07d164d Mon Sep 17 00:00:00 2001 From: cypas Date: Wed, 17 Jul 2024 00:43:33 +0800 Subject: [PATCH 10/17] =?UTF-8?q?perf:=E9=99=8D=E4=BD=8Estat=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py b/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py index 85a6d97..390670a 100644 --- a/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py +++ b/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py @@ -39,7 +39,7 @@ async def sync_stat_ink(): error_cnt = 0 else_error_cnt = 0 notice_error_cnt = 0 - _pool = 20 + _pool = 15 for i in range(0, len(db_users), _pool): pool_users_list = db_users[i:i + _pool] tasks = [sync_stat_ink_func(db_user) for db_user in pool_users_list] @@ -92,7 +92,7 @@ async def sync_stat_ink_func(db_user: UserTable): try: await notify_to_private(db_user.platform, db_user.user_id, msg) except Exception as e: - cron_logger.error(f"db_user_id:{db_user.user_id} private notice error: {e}") + cron_logger.error(f"db_user_id:{db_user.id} private notice error: {e}") is_notice_error = True if error_msg: From 45c6f22320b672fe9f93bf1c44411652e31dc21c Mon Sep 17 00:00:00 2001 From: cypas Date: Mon, 22 Jul 2024 19:15:23 +0800 Subject: [PATCH 11/17] =?UTF-8?q?perf:stat=E5=90=8C=E6=AD=A5=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E5=AF=B9=E7=94=A8=E6=88=B7=E5=B1=8F=E8=94=BD=E6=83=85?= =?UTF-8?q?=E5=86=B5=E5=AE=9E=E6=97=B6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handle/cron/stat_ink.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py b/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py index 390670a..748069b 100644 --- a/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py +++ b/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py @@ -14,7 +14,7 @@ from ...utils import proxy_address, convert_td from ...utils.utils import DIR_RESOURCE, init_path, get_msg_id from ...data.data_source import model_get_all_stat_user, dict_clear_user_info_dict, global_user_info_dict, \ - dict_get_or_set_user_info + dict_get_or_set_user_info, model_get_or_set_user from ..send_msg import notify_to_private, report_notify_to_channel, cron_notify_to_channel from .utils import user_remove_duplicates, cron_logger from ...utils.bot import Kook_ActionFailed @@ -39,7 +39,7 @@ async def sync_stat_ink(): error_cnt = 0 else_error_cnt = 0 notice_error_cnt = 0 - _pool = 15 + _pool = 40 for i in range(0, len(db_users), _pool): pool_users_list = db_users[i:i + _pool] tasks = [sync_stat_ink_func(db_user) for db_user in pool_users_list] @@ -80,6 +80,10 @@ async def sync_stat_ink_func(db_user: UserTable): return is_complete, is_upload, is_error, is_notice_error, is_else_error msg, error_msg = res + + platform = db_user.platform + user_id = db_user.user_id + msg_id = get_msg_id(platform, user_id) is_complete = True if msg: is_upload = True @@ -91,8 +95,15 @@ async def sync_stat_ink_func(db_user: UserTable): msg += "\n/stat_notify close 关闭stat.ink同步情况推送" try: await notify_to_private(db_user.platform, db_user.user_id, msg) + except Kook_ActionFailed as e: + if e.status_code == 40000: + if e.message.startswith("你已被对方屏蔽"): + model_get_or_set_user(platform, user_id, stat_notify=0, report_notify=0) + cron_logger.warning( + f'sync_stat_ink send private error:db_user_id:{db_user.id},mgs_id:{msg_id},error:用户已屏蔽发信bot,已关闭其通知权限') + is_notice_error = True except Exception as e: - cron_logger.error(f"db_user_id:{db_user.id} private notice error: {e}") + cron_logger.error(f"sync_stat_ink send private error:db_user_id:{db_user.id},mgs_id:{msg_id},error: {e}") is_notice_error = True if error_msg: From 8920f870d412e2cec44f3e5cc2de62e8d999556f Mon Sep 17 00:00:00 2001 From: cypas Date: Mon, 22 Jul 2024 19:36:53 +0800 Subject: [PATCH 12/17] =?UTF-8?q?perf:qq=5Fmd=E7=9B=B8=E5=85=B3=E5=8F=98?= =?UTF-8?q?=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/handle/login.py | 8 +- nonebot_plugin_splatoon3_nso/handle/qq_md.py | 151 ++++++++++--------- 2 files changed, 82 insertions(+), 77 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/handle/login.py b/nonebot_plugin_splatoon3_nso/handle/login.py index 1b02ab2..107bae0 100644 --- a/nonebot_plugin_splatoon3_nso/handle/login.py +++ b/nonebot_plugin_splatoon3_nso/handle/login.py @@ -32,15 +32,15 @@ async def login_in(bot: Bot, event: Event, matcher: Matcher): # 只有q平台 且 q群才发md if isinstance(bot, QQ_Bot): - if isinstance(event, QQ_GME) and plugin_config.splatoon3_qq_md_mode: + if isinstance(event, (QQ_GME, QQ_C2CME)) and plugin_config.splatoon3_qq_md_mode: if isinstance(event, QQ_C2CME): user_id = "" # 发送md await bot_send_login_md(bot, event, user_id) await matcher.finish() - elif isinstance(event, QQ_C2CME): - pass - elif not isinstance(event, (QQ_GME, QQ_C2CME)): + # elif isinstance(event, QQ_C2CME): + # pass + else: msg = "QQ平台当前无法完成nso登录流程,请至其他平台完成登录后使用/getlc命令获取绑定码\n" \ f"Kook服务器id:{plugin_config.splatoon3_kk_guild_id}" await bot_send(bot, event, msg) diff --git a/nonebot_plugin_splatoon3_nso/handle/qq_md.py b/nonebot_plugin_splatoon3_nso/handle/qq_md.py index a72c22f..10dac3b 100644 --- a/nonebot_plugin_splatoon3_nso/handle/qq_md.py +++ b/nonebot_plugin_splatoon3_nso/handle/qq_md.py @@ -1,6 +1,7 @@ import re from nonebot.adapters.qq.models import MessageKeyboard, MessageMarkdown +from nonebot.adapters.telegram.model import InlineKeyboardMarkup from ..config import plugin_config from ..data.utils import plugin_data @@ -10,6 +11,7 @@ def last_md(user_id, image_size: tuple, url: str) -> QQ_Msg: """为/last查询拼装md结构""" template_id = "102083290_1705920931" + keyboard_template_id = "102083290_1720695986" image_width, image_height = image_size text_start = "发送/nso帮助查看详细用法" # text_end作为公告消息 @@ -57,80 +59,83 @@ def last_md(user_id, image_size: tuple, url: str) -> QQ_Msg: # }) keyboard = MessageKeyboard.model_validate({ - "content": { - "rows": [{"buttons": [ - { - "id": "1", - "render_data": { - "label": f"{button_show}", - "visited_label": f"{button_show}", - "style": 0 - }, - "action": { - "type": 2, - "permission": { - "type": 2, - }, - "unsupport_tips": "客户端不支持", - "data": f"{button_cmd}", - } - } - - ]}, - {"buttons": [ - { - "id": "1", - "render_data": { - "label": f"{button_show2}", - "visited_label": f"{button_show2}", - "style": 0 - }, - "action": { - "type": 2, - "permission": { - "type": 2, - }, - "unsupport_tips": "客户端不支持", - "data": f"{button_cmd2}", - } - }, - { - "id": "1", - "render_data": { - "label": f"{button_show3}", - "visited_label": f"{button_show3}", - "style": 0 - }, - "action": { - "type": 2, - "permission": { - "type": 2, - }, - "unsupport_tips": "客户端不支持", - "data": f"{button_cmd3}", - } - }, - { - "id": "1", - "render_data": { - "label": f"{button_show4}", - "visited_label": f"{button_show4}", - "style": 0 - }, - "action": { - "type": 0, - "permission": { - "type": 2, - }, - "unsupport_tips": "客户端不支持", - "data": f"{button_cmd4}", - } - } - - ]} - ] - } + "id": f"{keyboard_template_id}" }) + # keyboard = MessageKeyboard.model_validate({ + # "content": { + # "rows": [{"buttons": [ + # { + # "id": "1", + # "render_data": { + # "label": f"{button_show}", + # "visited_label": f"{button_show}", + # "style": 0 + # }, + # "action": { + # "type": 2, + # "permission": { + # "type": 2, + # }, + # "unsupport_tips": "客户端不支持", + # "data": f"{button_cmd}", + # } + # } + # + # ]}, + # {"buttons": [ + # { + # "id": "1", + # "render_data": { + # "label": f"{button_show2}", + # "visited_label": f"{button_show2}", + # "style": 0 + # }, + # "action": { + # "type": 2, + # "permission": { + # "type": 2, + # }, + # "unsupport_tips": "客户端不支持", + # "data": f"{button_cmd2}", + # } + # }, + # { + # "id": "1", + # "render_data": { + # "label": f"{button_show3}", + # "visited_label": f"{button_show3}", + # "style": 0 + # }, + # "action": { + # "type": 2, + # "permission": { + # "type": 2, + # }, + # "unsupport_tips": "客户端不支持", + # "data": f"{button_cmd3}", + # } + # }, + # { + # "id": "1", + # "render_data": { + # "label": f"{button_show4}", + # "visited_label": f"{button_show4}", + # "style": 0 + # }, + # "action": { + # "type": 0, + # "permission": { + # "type": 2, + # }, + # "unsupport_tips": "客户端不支持", + # "data": f"{button_cmd4}", + # } + # } + # + # ]} + # ] + # } + # }) qq_msg = QQ_Msg([QQ_MsgSeg.markdown(md), QQ_MsgSeg.keyboard(keyboard)]) return qq_msg From 6a65fead1cce967a6c98bc015f285b3327318911 Mon Sep 17 00:00:00 2001 From: cypas Date: Tue, 6 Aug 2024 17:22:28 +0800 Subject: [PATCH 13/17] =?UTF-8?q?feat:=E6=9B=B4=E6=94=B9=E6=8C=87=E4=BB=A4?= =?UTF-8?q?=E5=85=9C=E5=BA=95=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/__init__.py b/nonebot_plugin_splatoon3_nso/__init__.py index 26a3ec0..0af9158 100644 --- a/nonebot_plugin_splatoon3_nso/__init__.py +++ b/nonebot_plugin_splatoon3_nso/__init__.py @@ -25,15 +25,17 @@ ) -@on_startswith(("/", "、"), priority=99, block=True).handle() +@on_startswith(("/", "、", " "), priority=99, block=True).handle() async def unknown_command(bot: Bot, event: Event, matcher: Matcher): logger.info(f'unknown_command from {event.get_event_name()}') msg = "" if plugin_config.splatoon3_unknown_command_fallback_reply: if isinstance(bot, Tg_Bot): msg = "Sorry, I didn't understand that command. /help" + elif isinstance(bot, QQ_Bot): + msg = "无效指令,请发送 /help 查看帮助\n或在消息框输入/后,手动选择bot命令" elif isinstance(bot, All_BOT): - msg = "无效指令,发送 /help 查看帮助" + msg = "无效指令,请发送 /help 查看帮助" kook_black_list = plugin_config.splatoon3_unknown_command_fallback_reply_kook_black_list if len(kook_black_list) > 0: if isinstance(bot, Kook_Bot): @@ -52,7 +54,7 @@ async def unknown_command(bot: Bot, event: Event, matcher: Matcher): async def c2c_unknown_command(bot: Bot, event: Event, matcher: Matcher): """为qq c2c任何未匹配文本进行兜底""" logger.info(f'unknown_command from {event.get_event_name()}') - msg = "无效指令,发送 /help 查看帮助" + msg = "无效指令,请发送/help 查看帮助\n或在消息框输入/后,手动选择bot命令" await matcher.finish(msg) From 936a8f60b3fe7852c00d7797d8c79e2ab303e458 Mon Sep 17 00:00:00 2001 From: cypas Date: Tue, 6 Aug 2024 18:59:35 +0800 Subject: [PATCH 14/17] =?UTF-8?q?perf:=E6=9B=B4=E6=94=B9=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E7=AD=89=E7=BA=A7=EF=BC=8C=E5=87=8F=E5=B0=91?= =?UTF-8?q?=E7=BB=88=E7=AB=AF=E7=9A=84=E6=97=A5=E5=BF=97=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handle/cron/event_top.py | 12 +-- .../handle/cron/report.py | 2 +- .../handle/cron/stat_ink.py | 95 +++++++++++-------- .../handle/cron/user_friends.py | 4 +- .../handle/cron/x_player.py | 6 +- 5 files changed, 70 insertions(+), 49 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/handle/cron/event_top.py b/nonebot_plugin_splatoon3_nso/handle/cron/event_top.py index 2077fa8..c64fc67 100644 --- a/nonebot_plugin_splatoon3_nso/handle/cron/event_top.py +++ b/nonebot_plugin_splatoon3_nso/handle/cron/event_top.py @@ -19,7 +19,7 @@ async def get_event_top(): db_user = model_get_newest_user() if not db_user: - cron_logger.info(f"no user login.") + cron_logger.error(f"no user login.") return user = dict_get_or_set_user_info(db_user.platform, db_user.user_id) splatoon = Splatoon(None, None, user) @@ -47,13 +47,13 @@ async def get_event_top_player_task(splatoon): for n in edges[::-1]: in_ed = n['node']['leagueMatchRankingTimePeriodGroups']['edges'] for nn in in_ed[::-1]: - cron_logger.info(nn['node']['leagueMatchSetting']['leagueMatchEvent']['name']) + cron_logger.debug(nn['node']['leagueMatchSetting']['leagueMatchEvent']['name']) for t in nn['node']['timePeriods']: top_id = t['id'] top_type = base64.b64decode(top_id).decode('utf-8') _, search_type = top_type.split('TimePeriod-') count = model_get_top_all_count_by_top_type(search_type) - cron_logger.info(f'top_all.type search {search_type}, {count or 0}') + cron_logger.debug(f'top_all.type search {search_type}, {count or 0}') if count: continue res = await splatoon.get_event_items(top_id, multiple=True) @@ -63,16 +63,16 @@ async def get_event_top_player_task(splatoon): def parse_league(league): """解析活动榜单并写入数据""" if not league: - cron_logger.info('no league') + cron_logger.debug('no league') return play_time = league['data']['rankingPeriod']['endTime'].replace('T', ' ').replace('Z', '') play_time = dt.strptime(play_time, '%Y-%m-%d %H:%M:%S') league_name = league['data']['rankingPeriod']['leagueMatchSetting']['leagueMatchEvent']['name'] - cron_logger.info(f'{play_time}, {league_name}') + cron_logger.debug(f'{play_time}, {league_name}') for team in league['data']['rankingPeriod']['teams']: top_id = team['id'] - cron_logger.info(f'saving top_id: {top_id}') + cron_logger.debug(f'saving top_id: {top_id}') model_delete_top_all(top_id) top_type = base64.b64decode(top_id).decode('utf-8') for n in team['details']['nodes']: diff --git a/nonebot_plugin_splatoon3_nso/handle/cron/report.py b/nonebot_plugin_splatoon3_nso/handle/cron/report.py index fa3d8a7..5de6001 100644 --- a/nonebot_plugin_splatoon3_nso/handle/cron/report.py +++ b/nonebot_plugin_splatoon3_nso/handle/cron/report.py @@ -234,7 +234,7 @@ async def send_report_task(): if msg: # 写日志 log_msg = msg.replace('\n', '') - report_logger.info(f"get {msg_id} report:{log_msg}") + report_logger.debug(f"get {msg_id} report:{log_msg}") # # 通知到频道 # await report_notify_to_channel(user.platform, user.user_id, msg, _type='job') # 通知到私信 diff --git a/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py b/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py index 748069b..7b6bbc0 100644 --- a/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py +++ b/nonebot_plugin_splatoon3_nso/handle/cron/stat_ink.py @@ -19,6 +19,9 @@ from .utils import user_remove_duplicates, cron_logger from ...utils.bot import Kook_ActionFailed +# 错误对局,没有会员,nso被ban,无效登录凭证 +expected_str_list = ["status: 500", "Membership required", "has be banned", "invalid_grant"] + async def sync_stat_ink(): """同步至stat""" @@ -34,18 +37,14 @@ async def sync_stat_ink(): # 去重 db_users = user_remove_duplicates(db_users) - complete_cnt = 0 - upload_cnt = 0 - error_cnt = 0 - else_error_cnt = 0 - notice_error_cnt = 0 + complete_cnt, upload_cnt, error_cnt, else_error_cnt, notice_error_cnt, battle_error_cnt, membership_error_cnt = 0, 0, 0, 0, 0, 0, 0 _pool = 40 for i in range(0, len(db_users), _pool): pool_users_list = db_users[i:i + _pool] tasks = [sync_stat_ink_func(db_user) for db_user in pool_users_list] res = await asyncio.gather(*tasks) for r in res: - is_complete, is_upload, is_error, is_notice_error, is_else_error = r + is_complete, is_upload, is_error, is_notice_error, is_else_error, is_battle_error, is_membership_error = r if is_complete: complete_cnt += 1 if is_upload: @@ -56,28 +55,32 @@ async def sync_stat_ink(): notice_error_cnt += 1 if is_else_error: else_error_cnt += 1 + if is_battle_error: + battle_error_cnt += 1 + if is_membership_error: + membership_error_cnt += 1 # 耗时 str_time = convert_td(dt.utcnow() - t) cron_msg = (f"sync_stat_ink end: {str_time}\n" f"complete_cnt: {complete_cnt}, upload_cnt: {upload_cnt}\n" - f"error_cnt: {error_cnt},notice_error_cnt: {notice_error_cnt}") + f"error_cnt: {error_cnt},battle_error_cnt: {battle_error_cnt},membership_error_cnt: {membership_error_cnt},notice_error_cnt: {notice_error_cnt}") cron_logger.info(cron_msg) notice_msg = (f"耗时:{str_time}\n完成: {complete_cnt},同步: {upload_cnt}\n" - f"错误: {error_cnt},通知错误: {notice_error_cnt}") + f"错误: {error_cnt},对战错误: {battle_error_cnt},缺少会员: {membership_error_cnt},通知错误: {notice_error_cnt}") await cron_notify_to_channel("sync_stat_ink", "end", notice_msg) async def sync_stat_ink_func(db_user: UserTable): """同步stat.ink""" - is_complete, is_upload, is_error, is_else_error, is_notice_error = False, False, False, False, False + is_complete, is_upload, is_error, is_else_error, is_notice_error, is_battle_error, is_membership_error = False, False, False, False, False, False, False cron_logger.debug(f"get user: {db_user.user_name}, have stat_key: {db_user.stat_key}") res = await get_post_stat_msg(db_user) if not isinstance(res, tuple): is_else_error = True - return is_complete, is_upload, is_error, is_notice_error, is_else_error + return is_complete, is_upload, is_error, is_notice_error, is_else_error, is_battle_error, is_membership_error msg, error_msg = res @@ -98,18 +101,23 @@ async def sync_stat_ink_func(db_user: UserTable): except Kook_ActionFailed as e: if e.status_code == 40000: if e.message.startswith("你已被对方屏蔽"): - model_get_or_set_user(platform, user_id, stat_notify=0, report_notify=0) + dict_get_or_set_user_info(platform, user_id, stat_notify=0, report_notify=0) cron_logger.warning( f'sync_stat_ink send private error:db_user_id:{db_user.id},mgs_id:{msg_id},error:用户已屏蔽发信bot,已关闭其通知权限') is_notice_error = True except Exception as e: - cron_logger.error(f"sync_stat_ink send private error:db_user_id:{db_user.id},mgs_id:{msg_id},error: {e}") + cron_logger.error( + f"sync_stat_ink send private error:db_user_id:{db_user.id},mgs_id:{msg_id},error: {e}") is_notice_error = True if error_msg: is_error = True + if "status: 500" in error_msg: + is_battle_error = True + if "Membership required" in error_msg: + is_membership_error = True - return is_complete, is_upload, is_error, is_notice_error, is_else_error + return is_complete, is_upload, is_error, is_notice_error, is_else_error, is_battle_error, is_membership_error async def get_post_stat_msg(db_user): @@ -156,25 +164,32 @@ async def get_post_stat_msg(db_user): if not isinstance(res, tuple): return battle_cnt, coop_cnt, url, error_msg = res + + flag_need_retry = True # f-api重试 if error_msg: - # 判断重试时的对象名称以及f地址 - if f_gen_url == F_GEN_URL: - now_f_str = "F_URL" - next_f_str = "F_URL_2" - next_f_url = F_GEN_URL_2 - else: - now_f_str = "F_URL_2" - next_f_str = "F_URL" - next_f_url = F_GEN_URL - cron_logger.warning(f"{db_user.id}, {db_user.user_name}, {now_f_str} Error,try {next_f_str} again") - res = exported_to_stat_ink(db_user.id, u.session_token, db_user.stat_key, next_f_url, - g_token=u.g_token, - bullet_token=u.bullet_token) - - if not isinstance(res, tuple): - return - battle_cnt, coop_cnt, url, error_msg = res + # 排除预期错误 + for expected_str in expected_str_list: + if expected_str in error_msg: + flag_need_retry = False + if flag_need_retry: + # 判断重试时的对象名称以及f地址 + if f_gen_url == F_GEN_URL: + now_f_str = "F_URL" + next_f_str = "F_URL_2" + next_f_url = F_GEN_URL_2 + else: + now_f_str = "F_URL_2" + next_f_str = "F_URL" + next_f_url = F_GEN_URL + cron_logger.warning(f"{db_user.id}, {db_user.user_name}, {now_f_str} Error,try {next_f_str} again") + res = exported_to_stat_ink(db_user.id, u.session_token, db_user.stat_key, next_f_url, + g_token=u.g_token, + bullet_token=u.bullet_token) + + if not isinstance(res, tuple): + return + battle_cnt, coop_cnt, url, error_msg = res msg = "" if battle_cnt or coop_cnt: @@ -289,10 +304,6 @@ def exported_to_stat_ink(user_id, session_token, api_key, f_gen_url, user_lang=" cmds.append(f"""sed -i 's/gToken[^,]*,/gToken\": \"{g_token}\",/' {path_config_file}""") cmds.append(f"""sed -i 's/bulletToken[^,]*,/bulletToken\": \"{bullet_token}\",/' {path_config_file}""") - for cmd in cmds: - cron_logger.debug(f'user_db_id:{user_id} cli: {cmd}') - os.system(cmd) - env = {} # deno代理配置 # http @@ -306,7 +317,7 @@ def exported_to_stat_ink(user_id, session_token, api_key, f_gen_url, user_lang=" # run deno cmd = f'{deno_path} run -Ar ./s3si.ts -n -p {path_config_file}' - cron_logger.info(cmd) + cron_logger.debug(cmd) res = "" error = "" @@ -323,6 +334,8 @@ def exported_to_stat_ink(user_id, session_token, api_key, f_gen_url, user_lang=" except subprocess.TimeoutExpired: error_msg = f"deno run timeout\n" except Exception as e: + if "html" in e: + e = "html error" error_msg = f"deno run err:\n{e}" if error: # error里面混有deno debug内容,需要经过过滤 @@ -336,10 +349,18 @@ def exported_to_stat_ink(user_id, session_token, api_key, f_gen_url, user_lang=" error_msg += f"{line}\n" if error_msg: - cron_logger.error(f'user_db_id:{user_id} deno cli error,result:\n{error_msg}') + expect = "" + for expected_str in expected_str_list: + if expected_str in error_msg: + expect = expected_str + break + if expect: + cron_logger.error(f'user_db_id:{user_id} deno cli error,result: {expect}') + else: + cron_logger.error(f'user_db_id:{user_id} deno cli unexpected error,result:\n{error_msg}') elif res: # success - cron_logger.info(f'user_db_id:{user_id} deno cli success,result:\n{res}') + cron_logger.debug(f'user_db_id:{user_id} deno cli success,result:\n{res}') for line in res.split('\n'): line = line.strip() diff --git a/nonebot_plugin_splatoon3_nso/handle/cron/user_friends.py b/nonebot_plugin_splatoon3_nso/handle/cron/user_friends.py index 8cdb076..393bc6e 100644 --- a/nonebot_plugin_splatoon3_nso/handle/cron/user_friends.py +++ b/nonebot_plugin_splatoon3_nso/handle/cron/user_friends.py @@ -35,7 +35,7 @@ async def create_get_user_friends_tasks(): continue model_set_user_friend(f_list) friends_count += len(f_list) - cron_logger.info(f"get friends: {friends_count}") + cron_logger.info(f"get all friends count: {friends_count}") # 耗时 str_time = convert_td(dt.utcnow() - t) cron_msg = f"create_get_user_friends_tasks end: {str_time}\nget friends: {friends_count}" @@ -67,7 +67,7 @@ async def get_friends_task(p_and_id): friend_id = f['id'] player_name = f.get('playerName') or '' nickname = f.get('nickname') or '' - cron_logger.info(f'get_friend: {msg_id},{u.game_name}--sp:{player_name} ,ns:{nickname}') + cron_logger.debug(f'get_friend: {msg_id},{u.game_name}--sp:{player_name} ,ns:{nickname}') user_icon = f['userIcon']['url'] f_list.append((user_id, friend_id, player_name, nickname, user_icon)) diff --git a/nonebot_plugin_splatoon3_nso/handle/cron/x_player.py b/nonebot_plugin_splatoon3_nso/handle/cron/x_player.py index 68e8f82..b972802 100644 --- a/nonebot_plugin_splatoon3_nso/handle/cron/x_player.py +++ b/nonebot_plugin_splatoon3_nso/handle/cron/x_player.py @@ -67,7 +67,7 @@ async def parse_x_data(top_id, splatoon): try: await get_top_x(first_rows, top_id, x_type, hash_mode, splatoon) except Exception as ex: - cron_logger.exception(f'get_top_x error: {top_id}, {x_type}, error:{ex}') + cron_logger.error(f'get_top_x error: {top_id}, {x_type}, error:{ex}') continue time.sleep(5) @@ -100,7 +100,7 @@ async def get_top_x(data_row, top_id, x_type, mode_hash, splatoon=None): cursor = _res['data']['node'][f'xRanking{x_type}']['pageInfo']['endCursor'] has_next_page = _res['data']['node'][f'xRanking{x_type}']['pageInfo']['hasNextPage'] - cron_logger.info(f'get page: {cursor}, {has_next_page}') + cron_logger.debug(f'get page: {cursor}, {has_next_page}') if not has_next_page: break @@ -132,7 +132,7 @@ async def get_top_x(data_row, top_id, x_type, mode_hash, splatoon=None): cursor = _res['data']['node'][f'xRanking{x_type}']['pageInfo']['endCursor'] has_next_page = _res['data']['node'][f'xRanking{x_type}']['pageInfo']['hasNextPage'] - cron_logger.info(f'get page: {cursor}, {has_next_page}') + cron_logger.debug(f'get page: {cursor}, {has_next_page}') if not has_next_page: break From 54d966ab1184ab06f396d634a8cd9abd4f555b82 Mon Sep 17 00:00:00 2001 From: cypas Date: Wed, 7 Aug 2024 00:50:32 +0800 Subject: [PATCH 15/17] =?UTF-8?q?perf:=E6=9B=B4=E6=94=B9=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E7=AD=89=E7=BA=A7=EF=BC=8C=E5=87=8F=E5=B0=91?= =?UTF-8?q?=E7=BB=88=E7=AB=AF=E7=9A=84=E6=97=A5=E5=BF=97=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/handle/last.py | 10 ++++------ nonebot_plugin_splatoon3_nso/s3s/iksm.py | 2 +- nonebot_plugin_splatoon3_nso/s3s/splatoon.py | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/handle/last.py b/nonebot_plugin_splatoon3_nso/handle/last.py index dfb25cc..f307e1a 100644 --- a/nonebot_plugin_splatoon3_nso/handle/last.py +++ b/nonebot_plugin_splatoon3_nso/handle/last.py @@ -85,12 +85,10 @@ async def last(bot: Bot, event: Event, args: Message = CommandArg()): if not isinstance(bot, QQ_Bot): user = dict_get_or_set_user_info(platform, user_id) if user.push_cnt < 3: - logger.info(f'is_playing: {is_playing}') - if is_playing: - msg = '' - if user.push_cnt < 5: - msg = "正在游玩时可以 /push 开启推送模式~" - await bot_send(bot, event, msg) + logger.debug(f'is_playing: {is_playing}') + if is_playing and user.push_cnt < 5: + msg = "正在游玩时可以 /push 开启推送模式~" + await bot_send(bot, event, msg) async def get_last_battle_or_coop(bot, event, for_push=False, get_battle=False, get_coop=False, diff --git a/nonebot_plugin_splatoon3_nso/s3s/iksm.py b/nonebot_plugin_splatoon3_nso/s3s/iksm.py index 9265b12..288bad6 100644 --- a/nonebot_plugin_splatoon3_nso/s3s/iksm.py +++ b/nonebot_plugin_splatoon3_nso/s3s/iksm.py @@ -425,7 +425,7 @@ async def _get_g_token(self, access_token, f, uuid, timestamp, coral_user_id): web_service_token = web_service_resp["result"]["accessToken"] except: # retry once if code 9403/9599 error from nintendo - self.logger.warning(f"retry once if code 9403/9599 error from nintendo") + self.logger.debug(f"retry once if code 9403/9599 error from nintendo") try: f, uuid, timestamp = await self.f_api(access_token, 2, self.f_gen_url, self.r_user_id, coral_user_id=coral_user_id) diff --git a/nonebot_plugin_splatoon3_nso/s3s/splatoon.py b/nonebot_plugin_splatoon3_nso/s3s/splatoon.py index 1dfebd7..0e94126 100644 --- a/nonebot_plugin_splatoon3_nso/s3s/splatoon.py +++ b/nonebot_plugin_splatoon3_nso/s3s/splatoon.py @@ -196,7 +196,7 @@ def refresh_another_account(self): if len(users) > 0: for u in users: msg_id = get_msg_id(u.platform, u.user_id) - self.logger.info(f'another_account {u.id},{msg_id} tokens updated.') + self.logger.debug(f'another_account {u.id},{msg_id} tokens updated.') # 如果存在全局缓存,也更新缓存数据 key = get_msg_id(u.platform, u.user_id) user_info = global_user_info_dict.get(key) From cd67d349d4aa6c6f52b4f166af4893cd6df4a778 Mon Sep 17 00:00:00 2001 From: cypas Date: Wed, 7 Aug 2024 00:51:22 +0800 Subject: [PATCH 16/17] =?UTF-8?q?feat:=E6=9C=AA=E5=93=8D=E5=BA=94=E8=89=BE?= =?UTF-8?q?=E7=89=B9=E6=B6=88=E6=81=AF=E5=A2=9E=E5=8A=A0=E5=85=9C=E5=BA=95?= =?UTF-8?q?=E5=9B=9E=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/__init__.py b/nonebot_plugin_splatoon3_nso/__init__.py index 0af9158..9e6e1e2 100644 --- a/nonebot_plugin_splatoon3_nso/__init__.py +++ b/nonebot_plugin_splatoon3_nso/__init__.py @@ -1,6 +1,6 @@ from nonebot.message import event_preprocessor from nonebot.plugin import PluginMetadata -from nonebot.rule import is_type +from nonebot.rule import is_type, to_me from .config import driver, plugin_config, Config from .data.db_sqlite import init_db @@ -25,7 +25,8 @@ ) -@on_startswith(("/", "、", " "), priority=99, block=True).handle() +@on_message(rule=to_me(), priority=97, block=True).handle() +@on_startswith(("/", "、"), priority=99, block=True).handle() async def unknown_command(bot: Bot, event: Event, matcher: Matcher): logger.info(f'unknown_command from {event.get_event_name()}') msg = "" From b08b267009b15046f6e14d2b6b1040d618caba66 Mon Sep 17 00:00:00 2001 From: cypas Date: Wed, 7 Aug 2024 00:52:50 +0800 Subject: [PATCH 17/17] =?UTF-8?q?perf:qq=E5=BC=95=E5=AF=BC=E7=99=BB?= =?UTF-8?q?=E5=BD=95md=E6=94=B9=E7=94=A8=E6=A8=A1=E7=89=88id?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nonebot_plugin_splatoon3_nso/handle/qq_md.py | 49 +++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/nonebot_plugin_splatoon3_nso/handle/qq_md.py b/nonebot_plugin_splatoon3_nso/handle/qq_md.py index 10dac3b..037a52f 100644 --- a/nonebot_plugin_splatoon3_nso/handle/qq_md.py +++ b/nonebot_plugin_splatoon3_nso/handle/qq_md.py @@ -144,6 +144,7 @@ def last_md(user_id, image_size: tuple, url: str) -> QQ_Msg: def login_md(user_id, check_session=False) -> QQ_Msg: """无法使用login时转kook登录的md卡片提示""" template_id = "102083290_1705923685" + keyboard_template_id = "102083290_1721647351" data1 = "" if check_session: data1 += "nso未登录,无法使用相关查询," @@ -167,29 +168,33 @@ def login_md(user_id, check_session=False) -> QQ_Msg: }) keyboard = MessageKeyboard.model_validate({ - "content": { - "rows": [{"buttons": [ - { - "id": "1", - "render_data": { - "label": f"{button_show}", - "visited_label": f"{button_show}", - "style": 0 - }, - "action": { - "type": 0, - "permission": { - "type": 2, - }, - "unsupport_tips": "客户端不支持", - "data": f"{kook_jump_link}", - } - } - - ]}, - ] - } + "id": f"{keyboard_template_id}" }) + + # keyboard = MessageKeyboard.model_validate({ + # "content": { + # "rows": [{"buttons": [ + # { + # "id": "1", + # "render_data": { + # "label": f"{button_show}", + # "visited_label": f"{button_show}", + # "style": 0 + # }, + # "action": { + # "type": 0, + # "permission": { + # "type": 2, + # }, + # "unsupport_tips": "客户端不支持", + # "data": f"{kook_jump_link}", + # } + # } + # + # ]}, + # ] + # } + # }) qq_msg = QQ_Msg([QQ_MsgSeg.markdown(md), QQ_MsgSeg.keyboard(keyboard)]) return qq_msg