feat(ui): 新增一键上传日志报告功能
This commit is contained in:
parent
b11126e426
commit
62d5805b96
|
@ -276,7 +276,7 @@ def clear_saved():
|
|||
logger.info("Clearing debug saved files...")
|
||||
if debug.auto_save_to_folder:
|
||||
try:
|
||||
shutil.rmtree(debug.auto_save_to_folder)
|
||||
shutil.rmtree(debug.auto_save_to_folder, ignore_errors=True)
|
||||
logger.info(f"Cleared debug saved files: {debug.auto_save_to_folder}")
|
||||
except PermissionError:
|
||||
logger.error(f"Failed to clear debug saved files: {debug.auto_save_to_folder}")
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import requests
|
||||
import os
|
||||
|
||||
def upload(file_path: str) -> str:
|
||||
"""
|
||||
上传文件到 paste.sensio.no
|
||||
|
||||
Args:
|
||||
file_path: 要上传的文件路径
|
||||
|
||||
Returns:
|
||||
str: 上传后的 URL
|
||||
"""
|
||||
url = 'https://paste.sensio.no/'
|
||||
headers = {
|
||||
'accept': 'text/plain',
|
||||
'User-Agent': 'KAA',
|
||||
'x-uuid': ''
|
||||
}
|
||||
|
||||
files = {
|
||||
'file': (os.path.basename(file_path), open(file_path, 'rb'))
|
||||
}
|
||||
|
||||
response = requests.post(url, files=files, headers=headers, allow_redirects=False)
|
||||
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Upload failed with status code {response.status_code}")
|
||||
|
||||
return response.text.strip()
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_file = "version"
|
||||
if os.path.exists(test_file):
|
||||
result = upload(test_file)
|
||||
print(f"Upload result: {result}")
|
|
@ -0,0 +1,54 @@
|
|||
import requests
|
||||
import os
|
||||
|
||||
def upload(file_path: str) -> str:
|
||||
url = 'https://tmpsend.com/upload'
|
||||
headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
|
||||
'Referer': 'https://tmpsend.com/',
|
||||
}
|
||||
|
||||
file_name = os.path.basename(file_path)
|
||||
file_size = os.path.getsize(file_path)
|
||||
|
||||
# 第一次请求:添加文件信息
|
||||
files = {
|
||||
'action': (None, 'add'),
|
||||
'name': (None, file_name),
|
||||
'size': (None, str(file_size)),
|
||||
'file': (file_name, open(file_path, 'rb'))
|
||||
}
|
||||
|
||||
response = requests.post(url, headers=headers, files=files)
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Upload failed with status code {response.status_code}")
|
||||
|
||||
result = response.json()
|
||||
if result.get('hasError'):
|
||||
raise Exception(result.get('error'))
|
||||
|
||||
file_id = result.get('id')
|
||||
if not file_id:
|
||||
raise Exception("Failed to get file ID")
|
||||
|
||||
# 第二次请求:上传实际文件
|
||||
upload_files = {
|
||||
'action': (None, 'upload'),
|
||||
'id': (None, file_id),
|
||||
'name': (None, file_name),
|
||||
'size': (None, str(file_size)),
|
||||
'start': (None, '0'),
|
||||
'end': (None, str(file_size)),
|
||||
'data': (file_name, open(file_path, 'rb'), 'application/octet-stream')
|
||||
}
|
||||
|
||||
upload_response = requests.post(url, headers=headers, files=upload_files)
|
||||
if upload_response.status_code != 200:
|
||||
raise Exception(f"File upload failed with status code {upload_response.status_code}")
|
||||
|
||||
return 'https://tmpsend.com/' + file_id
|
||||
|
||||
if __name__ == "__main__":
|
||||
file_path = r"主题1.thmx"
|
||||
print(upload(file_path))
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
import os
|
||||
import zipfile
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import List, Dict, Tuple, Literal
|
||||
from functools import partial
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Dict, Tuple, Literal, Generator
|
||||
import importlib.metadata
|
||||
|
||||
import cv2
|
||||
import gradio as gr
|
||||
|
||||
from kotonebot.backend.context import task_registry
|
||||
|
@ -34,6 +36,95 @@ root_logger.addHandler(file_handler)
|
|||
|
||||
logging.getLogger("kotonebot").setLevel(logging.DEBUG)
|
||||
|
||||
def _save_bug_report(
|
||||
path: str
|
||||
) -> Generator[str, None, str]:
|
||||
"""
|
||||
保存错误报告
|
||||
|
||||
:param path: 保存的路径。若为 `None`,则保存到 `./reports/{YY-MM-DD HH-MM-SS}.zip`。
|
||||
:return: 保存的路径
|
||||
"""
|
||||
from kotonebot import device
|
||||
from kotonebot.backend.context import ContextStackVars
|
||||
|
||||
# 确保目录存在
|
||||
os.makedirs('logs', exist_ok=True)
|
||||
os.makedirs('reports', exist_ok=True)
|
||||
|
||||
error = ""
|
||||
with zipfile.ZipFile(path, 'w', zipfile.ZIP_DEFLATED, compresslevel=9) as zipf:
|
||||
# 打包截图
|
||||
yield "### 打包上次截图..."
|
||||
try:
|
||||
stack = ContextStackVars.current()
|
||||
screenshot = None
|
||||
if stack is not None:
|
||||
screenshot = stack._screenshot
|
||||
if screenshot is not None:
|
||||
img = cv2.imencode('.png', screenshot)[1].tobytes()
|
||||
zipf.writestr('last_screenshot.png', img)
|
||||
if screenshot is None:
|
||||
error += "无上次截图数据\n"
|
||||
except Exception as e:
|
||||
error += f"保存上次截图失败:{str(e)}\n"
|
||||
|
||||
# 打包当前截图
|
||||
yield "### 打包当前截图..."
|
||||
try:
|
||||
screenshot = device.screenshot()
|
||||
img = cv2.imencode('.png', screenshot)[1].tobytes()
|
||||
zipf.writestr('current_screenshot.png', img)
|
||||
except Exception as e:
|
||||
error += f"保存当前截图失败:{str(e)}\n"
|
||||
|
||||
# 打包配置文件
|
||||
yield "### 打包配置文件..."
|
||||
try:
|
||||
with open('config.json', 'r', encoding='utf-8') as f:
|
||||
zipf.writestr('config.json', f.read())
|
||||
except Exception as e:
|
||||
error += f"保存配置文件失败:{str(e)}\n"
|
||||
|
||||
# 打包 logs 文件夹
|
||||
if os.path.exists('logs'):
|
||||
for root, dirs, files in os.walk('logs'):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
arcname = os.path.join('logs', os.path.relpath(file_path, 'logs'))
|
||||
zipf.write(file_path, arcname)
|
||||
yield f"### 打包 log 文件:{arcname}"
|
||||
|
||||
# 打包 reports 文件夹
|
||||
if os.path.exists('reports'):
|
||||
for root, dirs, files in os.walk('reports'):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
arcname = os.path.join('reports', os.path.relpath(file_path, 'reports'))
|
||||
zipf.write(file_path, arcname)
|
||||
yield f"### 打包 report 文件:{arcname}"
|
||||
|
||||
# 上传报告
|
||||
from .file_host.sensio import upload
|
||||
yield "### 上传报告..."
|
||||
url = ''
|
||||
try:
|
||||
url = upload(path)
|
||||
except Exception as e:
|
||||
yield f"### 上传报告失败:{str(e)}\n\n"
|
||||
return ''
|
||||
|
||||
final_msg = f"### 报告导出成功:{url}\n\n"
|
||||
expire_time = datetime.now() + timedelta(days=7)
|
||||
if error:
|
||||
final_msg += f"### 但发生了以下错误\n\n"
|
||||
final_msg += '\n* '.join(error.strip().split('\n'))
|
||||
final_msg += '\n'
|
||||
final_msg += f"### 此链接将于 {expire_time.strftime('%Y-%m-%d %H:%M:%S')}(7 天后)过期\n\n"
|
||||
final_msg += '### 复制以上文本并反馈给开发者'
|
||||
yield final_msg
|
||||
return path
|
||||
|
||||
class KotoneBotUI:
|
||||
def __init__(self) -> None:
|
||||
self.is_running: bool = False
|
||||
|
@ -232,6 +323,7 @@ class KotoneBotUI:
|
|||
with gr.Row():
|
||||
run_btn = gr.Button("启动", scale=1)
|
||||
debug_btn = gr.Button("调试", scale=1)
|
||||
gr.Markdown("脚本报错或者卡住?点击“日志”选项卡中的“一键导出报告”可以快速反馈!")
|
||||
|
||||
task_status = gr.Dataframe(
|
||||
headers=["任务", "状态"],
|
||||
|
@ -577,6 +669,8 @@ class KotoneBotUI:
|
|||
with gr.Row():
|
||||
export_dumps_btn = gr.Button("导出 dump")
|
||||
export_logs_btn = gr.Button("导出日志")
|
||||
with gr.Row():
|
||||
save_report_btn = gr.Button("一键导出报告")
|
||||
result_text = gr.Markdown("等待操作\n\n\n")
|
||||
|
||||
export_dumps_btn.click(
|
||||
|
@ -587,6 +681,10 @@ class KotoneBotUI:
|
|||
fn=self.export_logs,
|
||||
outputs=[result_text]
|
||||
)
|
||||
save_report_btn.click(
|
||||
fn=partial(_save_bug_report, path='report.zip'),
|
||||
outputs=[result_text]
|
||||
)
|
||||
|
||||
def _load_config(self) -> None:
|
||||
# 加载配置文件
|
||||
|
@ -624,4 +722,4 @@ def main() -> None:
|
|||
app.launch(inbrowser=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
|
Loading…
Reference in New Issue