Add notification sender and update item routes
This commit is contained in:
2
pkg/sender/__init__.py
Normal file
2
pkg/sender/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from .wechat_bot import WeChatBot
|
||||
from .server_chan import ServerChatBot
|
||||
42
pkg/sender/server_chan.py
Normal file
42
pkg/sender/server_chan.py
Normal 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
102
pkg/sender/wechat_bot.py
Normal 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")
|
||||
@@ -1,2 +0,0 @@
|
||||
class SmsBao():
|
||||
async def get
|
||||
@@ -1,10 +1,11 @@
|
||||
from typing import Any, NoReturn, TYPE_CHECKING
|
||||
from typing import Any, NoReturn
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
from starlette.status import (
|
||||
HTTP_400_BAD_REQUEST,
|
||||
HTTP_401_UNAUTHORIZED,
|
||||
HTTP_402_PAYMENT_REQUIRED,
|
||||
HTTP_403_FORBIDDEN,
|
||||
HTTP_404_NOT_FOUND,
|
||||
HTTP_409_CONFLICT,
|
||||
@@ -12,13 +13,9 @@ from starlette.status import (
|
||||
HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
HTTP_501_NOT_IMPLEMENTED,
|
||||
HTTP_503_SERVICE_UNAVAILABLE,
|
||||
HTTP_504_GATEWAY_TIMEOUT, HTTP_402_PAYMENT_REQUIRED,
|
||||
HTTP_504_GATEWAY_TIMEOUT,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
|
||||
|
||||
# --- Request and Response Helpers ---
|
||||
|
||||
def ensure_request_param(to_check: Any, detail: str) -> None:
|
||||
|
||||
Reference in New Issue
Block a user