Add notification sender and update item routes

This commit is contained in:
2025-10-06 01:03:52 +08:00
parent cd35c6fbed
commit a71cde7b82
12 changed files with 243 additions and 107 deletions

2
pkg/sender/__init__.py Normal file
View File

@@ -0,0 +1,2 @@
from .wechat_bot import WeChatBot
from .server_chan import ServerChatBot

42
pkg/sender/server_chan.py Normal file
View File

@@ -0,0 +1,42 @@
from typing import Literal
from loguru import logger
from model import Setting
from sqlmodel.ext.asyncio.session import AsyncSession
from pkg.utils import raise_internal_error, raise_service_unavailable
import aiohttp
class ServerChatBot:
async def get_url(session: AsyncSession):
server_chan_key = await Setting.get(session, Setting.name == "server_chan_key")
if not server_chan_key.value:
raise_internal_error("Server酱未配置请联系管理员")
url = f"https://sctapi.ftqq.com/{server_chan_key.value}.send"
return url
async def send_text(
session: AsyncSession,
title: str,
description: str,
) -> None:
"""发送的 Markdown 消息。
Args:
session (AsyncSession): 数据库会话
title (str): 需要发送的标题
description (str): 需要发送的文本消息
"""
async with aiohttp.ClientSession() as http_session:
async with http_session.post(
url=await ServerChatBot.get_url(session),
data={
"title": title,
"desp": description
}
) as response:
if response.status != 200:
logger.error(f"Failed to send to Server Chan: {response.status}")
raise_internal_error("Server酱服务不可用请稍后再试")
else:
logger.info("Server Chan message sent successfully")

102
pkg/sender/wechat_bot.py Normal file
View File

@@ -0,0 +1,102 @@
from typing import Literal
from loguru import logger
from model import Setting
from sqlmodel.ext.asyncio.session import AsyncSession
from pkg.utils import raise_internal_error, raise_service_unavailable
import aiohttp
webhook_url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send"
class WeChatBot:
async def get_key(session: AsyncSession):
key = await Setting.get(session, Setting.name == "wechat_bot_key")
if not key.value:
raise_internal_error("企业微信机器人未配置,请联系管理员")
return key.value
async def send_text(
session: AsyncSession,
text: str,
mentioned_all: bool = False,
mentioned_list: list[str] = [],
mentioned_mobile_list: list[str] = []
) -> None:
"""发送文本类型的消息。
Args:
session (AsyncSession): 数据库会话
text (str): 需要发送的文本消息
mentioned_all (bool, optional): 是否提及所有人 Defaults to False.
mentioned_list (list[str], optional): 提及的用户列表 Defaults to [].
mentioned_mobile_list (list[str], optional): 提及的手机号码列表 Defaults to [].
"""
key = await WeChatBot.get_key(session)
async with aiohttp.ClientSession() as http_session:
async with http_session.post(
url=f"{webhook_url}?key={key}",
json={
"msgtype": "text",
"text": {
"content": text
},
"mentioned_list": ["@all"] if mentioned_all else mentioned_list,
"mentioned_mobile_list": ["@all"] if mentioned_all else mentioned_mobile_list
}
) as response:
if response.status != 200:
logger.error(f"Failed to send WeChat message: {response.status}")
raise_internal_error("企业微信机器人服务不可用,请稍后再试")
else:
resp_json = await response.json()
if resp_json.get("errcode") != 0:
logger.error(f"WeChat API error: {resp_json.get('errmsg')}")
raise_service_unavailable("发送企业微信消息失败,请稍后再试或联系管理员")
else:
logger.info("WeChat message sent successfully")
async def send_markdown(
session: AsyncSession,
markdown: str,
version: Literal['v1', 'v2'],
mentioned_all: bool = False,
mentioned_list: list[str] = [],
mentioned_mobile_list: list[str] = []
) -> None:
key = await WeChatBot.get_key(session)
if version == 'v1':
payload = {
"msgtype": "markdown",
"markdown": {
"content": markdown,
"mentioned_list": ["@all"] if mentioned_all else mentioned_list,
"mentioned_mobile_list": ["@all"] if mentioned_all else mentioned_mobile_list
}
}
elif version == 'v2':
payload = {
"msgtype": "markdown_v2",
"markdown_v2": {
"content": markdown,
"mentioned_list": ["@all"] if mentioned_all else mentioned_list,
"mentioned_mobile_list": ["@all"] if mentioned_all else mentioned_mobile_list
}
}
async with aiohttp.ClientSession() as http_session:
async with http_session.post(
url=f"{webhook_url}?key={key}",
json=payload
) as response:
if response.status != 200:
logger.error(f"Failed to send WeChat message: {response.status}")
raise_internal_error("企业微信机器人服务不可用,请稍后再试")
else:
resp_json = await response.json()
if resp_json.get("errcode") != 0:
logger.error(f"WeChat API error: {resp_json.get('errmsg')}")
raise_service_unavailable("发送企业微信消息失败,请稍后再试或联系管理员")
else:
logger.info("WeChat message sent successfully")