diff --git a/.env.example b/.env.example index 50ffa27..9a79d78 100644 --- a/.env.example +++ b/.env.example @@ -3,4 +3,7 @@ UID= USER_NO= SCHOOL_ACCOUNT= USER_NAME= -WECHAT_COMANY_BOT_KEY= \ No newline at end of file +WECHAT_COMANY_BOT_KEY= +WEBHOOK_API_URL= +WEBHOOK_GROUP_ID= +WEBHOOK_API_TOKEN= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 546f5da..b747fb9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ __pycache__ data -fonts/*.ttf \ No newline at end of file +fonts/*.ttf +.env +.vscode \ No newline at end of file diff --git a/README.md b/README.md index e7663c3..1881c39 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ - 安装依赖 ```sh - pip install aiosqlite apscheduler httpx matplotlib dotenv + pip install aiosqlite apscheduler httpx matplotlib dotenv uvicorn ``` - 通过 `scheduler.py` 修改监控时长配置(不懂的请问 AI) diff --git a/config.py b/config.py index 63a677c..b91ca06 100644 --- a/config.py +++ b/config.py @@ -17,3 +17,8 @@ USER_NAME = os.getenv("USER_NAME", "张三") # 姓名 WECHAT_COMANY_BOT_KEY = os.getenv( "WECHAT_COMANY_BOT_KEY", "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" ) + +# Webhook 服务相关配置 +WEBHOOK_API_URL = os.getenv("WEBHOOK_API_URL", "http://127.0.0.1:8081") +WEBHOOK_API_TOKEN = os.getenv("WEBHOOK_API_TOKEN", "xxx") +WEBHOOK_GROUP_ID = os.getenv("WEBHOOK_GROUP_ID", "") diff --git a/notify/wechat_company.py b/notify/wechat_company.py index 5106c11..de0b25f 100644 --- a/notify/wechat_company.py +++ b/notify/wechat_company.py @@ -1,5 +1,6 @@ import httpx import config +from pathlib import Path async def send_image(base64_str: str, md5: str): @@ -10,3 +11,71 @@ async def send_image(base64_str: str, md5: str): async with httpx.AsyncClient(timeout=10) as client: resp = await client.post(url, json=payload) resp.raise_for_status() + + +async def upload_file(file_path: str, filename: str = "None") -> str: + """ + 上传文件到 webhook 服务的 /upload 端点 + + Args: + file_path: 本地文件路径 + filename: 上传时的文件名,不指定则使用原文件名 + + Returns: + 远程文件路径 + """ + if filename is None: + filename = Path(file_path).name + + url = f"{config.WEBHOOK_API_URL}/upload" + headers = {"Authorization": f"Bearer {config.WEBHOOK_API_TOKEN}"} + + with open(file_path, "rb") as f: + files = {"file": (filename, f)} + async with httpx.AsyncClient(timeout=30) as client: + resp = await client.post(url, files=files, headers=headers) + resp.raise_for_status() + data = resp.json() + return data.get("path", data.get("files", [None])[0]) + + +async def send_webhook(group_id: str, file_url: str) -> dict: + """ + 发送 webhook 请求通知文件 + + Args: + group_id: 群组 ID + file_url: 远程文件路径 + + Returns: + API 响应字典 + """ + url = f"{config.WEBHOOK_API_URL}/webhook" + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {config.WEBHOOK_API_TOKEN}", + } + + payload = {"group_id": group_id, "url": file_url, "type": "image"} + + async with httpx.AsyncClient(timeout=10) as client: + resp = await client.post(url, json=payload, headers=headers) + resp.raise_for_status() + return resp.json() + + +async def upload_and_notify(file_path: str, group_id: str, filename: str = "") -> dict: + """ + 上传文件并发送 webhook 通知(组合函数) + + Args: + file_path: 本地文件路径 + group_id: 群组 ID + filename: 上传时的文件名,不指定则使用原文件名 + + Returns: + API 响应字典 + """ + remote_path = await upload_file(file_path, filename) + result = await send_webhook(group_id, remote_path) + return result diff --git a/tasks/water_ammeter.py b/tasks/water_ammeter.py index 45f3b93..f9b7241 100644 --- a/tasks/water_ammeter.py +++ b/tasks/water_ammeter.py @@ -4,7 +4,7 @@ import logging import base64 import hashlib -from notify.wechat_company import send_image +from notify.wechat_company import send_image, upload_and_notify from services.render import render_ammeter_chart, render_water_chart from services import collector import config @@ -122,8 +122,11 @@ class SD: ele_img_path = await render_ammeter_chart() if ele_img_path: logger.info("生成电表图表成功: %s", ele_img_path) - ele_b64, ele_md5 = image_to_base64_md5(ele_img_path) - await send_image(ele_b64, ele_md5) + # ele_b64, ele_md5 = image_to_base64_md5(ele_img_path) + # await send_image(ele_b64, ele_md5) + await upload_and_notify( + ele_img_path, config.WEBHOOK_GROUP_ID, ele_img_path.split("/")[-1] + ) logger.info("电表图表推送完成") else: logger.warning("未生成电表图表,跳过推送") @@ -131,8 +134,13 @@ class SD: water_img_path = await render_water_chart() if water_img_path: logger.info("生成水表图表成功: %s", water_img_path) - water_b64, water_md5 = image_to_base64_md5(water_img_path) - await send_image(water_b64, water_md5) + # water_b64, water_md5 = image_to_base64_md5(water_img_path) + # await send_image(water_b64, water_md5) + await upload_and_notify( + water_img_path, + config.WEBHOOK_GROUP_ID, + water_img_path.split("/")[-1], + ) logger.info("水表图表推送完成") else: logger.warning("未生成水表图表,跳过推送")