mirror of
https://github.com/zhinianboke/xianyu-auto-reply.git
synced 2025-08-30 01:27:35 +08:00
新增通知渠道
This commit is contained in:
parent
7da13c379c
commit
0f6b756c25
@ -338,7 +338,7 @@ python Start.py
|
||||
- **多重安全验证** - 超级加密保护,防止误操作和数据泄露
|
||||
- **批量处理能力** - 支持批量确认发货,提高处理效率
|
||||
- **异常处理机制** - 完善的错误处理和重试机制,确保发货成功
|
||||
- **多渠道通知** - 支持QQ、钉钉、邮件等多种发货通知方式
|
||||
- **多渠道通知** - 支持QQ、钉钉、飞书、Bark、邮件等多种发货通知方式
|
||||
|
||||
### 👥 多用户系统
|
||||
- **用户注册登录** - 支持邮箱验证和图形验证码,安全可靠
|
||||
@ -362,10 +362,11 @@ python Start.py
|
||||
- **账号状态验证** - 自动检查cookies启用状态,确保搜索功能正常
|
||||
|
||||
### 📱 通知系统
|
||||
- **多渠道支持** - QQ、钉钉、邮件、微信、Telegram等6种通知方式
|
||||
- **多渠道支持** - QQ、钉钉、飞书、Bark、邮件、微信、Telegram等8种通知方式
|
||||
- **智能配置** - 可视化配置界面,支持复杂参数和加密设置
|
||||
- **实时推送** - 重要事件实时通知,及时了解系统状态
|
||||
- **通知模板** - 自定义通知内容和格式,个性化消息推送
|
||||
- **移动端支持** - Bark iOS推送,随时随地接收通知
|
||||
|
||||
### 🔐 安全特性
|
||||
- **Cookie安全管理** - 加密存储用户凭证,定期自动刷新
|
||||
|
@ -1863,6 +1863,12 @@ class XianyuLive:
|
||||
case 'ding_talk' | 'dingtalk':
|
||||
logger.info(f"📱 开始发送钉钉通知...")
|
||||
await self._send_dingtalk_notification(config_data, notification_msg)
|
||||
case 'feishu' | 'lark':
|
||||
logger.info(f"📱 开始发送飞书通知...")
|
||||
await self._send_feishu_notification(config_data, notification_msg)
|
||||
case 'bark':
|
||||
logger.info(f"📱 开始发送Bark通知...")
|
||||
await self._send_bark_notification(config_data, notification_msg)
|
||||
case 'email':
|
||||
logger.info(f"📱 开始发送邮件通知...")
|
||||
await self._send_email_notification(config_data, notification_msg)
|
||||
@ -1989,6 +1995,159 @@ class XianyuLive:
|
||||
except Exception as e:
|
||||
logger.error(f"发送钉钉通知异常: {self._safe_str(e)}")
|
||||
|
||||
async def _send_feishu_notification(self, config_data: dict, message: str):
|
||||
"""发送飞书通知"""
|
||||
try:
|
||||
import aiohttp
|
||||
import json
|
||||
import hmac
|
||||
import hashlib
|
||||
import base64
|
||||
|
||||
logger.info(f"📱 飞书通知 - 开始处理配置数据: {config_data}")
|
||||
|
||||
# 解析配置
|
||||
webhook_url = config_data.get('webhook_url', '')
|
||||
secret = config_data.get('secret', '')
|
||||
|
||||
logger.info(f"📱 飞书通知 - Webhook URL: {webhook_url[:50]}...")
|
||||
logger.info(f"📱 飞书通知 - 是否有签名密钥: {'是' if secret else '否'}")
|
||||
|
||||
if not webhook_url:
|
||||
logger.warning("📱 飞书通知 - Webhook URL配置为空,无法发送通知")
|
||||
return
|
||||
|
||||
# 如果有加签密钥,生成签名
|
||||
timestamp = str(int(time.time()))
|
||||
sign = ""
|
||||
|
||||
if secret:
|
||||
string_to_sign = f'{timestamp}\n{secret}'
|
||||
hmac_code = hmac.new(
|
||||
secret.encode('utf-8'),
|
||||
string_to_sign.encode('utf-8'),
|
||||
digestmod=hashlib.sha256
|
||||
).digest()
|
||||
sign = base64.b64encode(hmac_code).decode('utf-8')
|
||||
logger.info(f"📱 飞书通知 - 已生成签名")
|
||||
|
||||
# 构建请求数据
|
||||
data = {
|
||||
"msg_type": "text",
|
||||
"content": {
|
||||
"text": message
|
||||
},
|
||||
"timestamp": timestamp
|
||||
}
|
||||
|
||||
# 如果有签名,添加到请求数据中
|
||||
if sign:
|
||||
data["sign"] = sign
|
||||
|
||||
logger.info(f"📱 飞书通知 - 请求数据构建完成")
|
||||
|
||||
# 发送POST请求
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(webhook_url, json=data, timeout=10) as response:
|
||||
response_text = await response.text()
|
||||
logger.info(f"📱 飞书通知 - 响应状态: {response.status}")
|
||||
logger.info(f"📱 飞书通知 - 响应内容: {response_text}")
|
||||
|
||||
if response.status == 200:
|
||||
try:
|
||||
response_json = json.loads(response_text)
|
||||
if response_json.get('code') == 0:
|
||||
logger.info(f"📱 飞书通知发送成功")
|
||||
else:
|
||||
logger.warning(f"📱 飞书通知发送失败: {response_json.get('msg', '未知错误')}")
|
||||
except json.JSONDecodeError:
|
||||
logger.info(f"📱 飞书通知发送成功(响应格式异常)")
|
||||
else:
|
||||
logger.warning(f"📱 飞书通知发送失败: HTTP {response.status}, 响应: {response_text}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"📱 发送飞书通知异常: {self._safe_str(e)}")
|
||||
import traceback
|
||||
logger.error(f"📱 飞书通知异常详情: {traceback.format_exc()}")
|
||||
|
||||
async def _send_bark_notification(self, config_data: dict, message: str):
|
||||
"""发送Bark通知"""
|
||||
try:
|
||||
import aiohttp
|
||||
import json
|
||||
from urllib.parse import quote
|
||||
|
||||
logger.info(f"📱 Bark通知 - 开始处理配置数据: {config_data}")
|
||||
|
||||
# 解析配置
|
||||
server_url = config_data.get('server_url', 'https://api.day.app').rstrip('/')
|
||||
device_key = config_data.get('device_key', '')
|
||||
title = config_data.get('title', '闲鱼自动回复通知')
|
||||
sound = config_data.get('sound', 'default')
|
||||
icon = config_data.get('icon', '')
|
||||
group = config_data.get('group', 'xianyu')
|
||||
url = config_data.get('url', '')
|
||||
|
||||
logger.info(f"📱 Bark通知 - 服务器: {server_url}")
|
||||
logger.info(f"📱 Bark通知 - 设备密钥: {device_key[:10]}..." if device_key else "📱 Bark通知 - 设备密钥: 未设置")
|
||||
logger.info(f"📱 Bark通知 - 标题: {title}")
|
||||
|
||||
if not device_key:
|
||||
logger.warning("📱 Bark通知 - 设备密钥配置为空,无法发送通知")
|
||||
return
|
||||
|
||||
# 构建请求URL和数据
|
||||
# Bark支持两种方式:URL路径方式和POST JSON方式
|
||||
# 这里使用POST JSON方式,更灵活且支持更多参数
|
||||
|
||||
api_url = f"{server_url}/push"
|
||||
|
||||
# 构建请求数据
|
||||
data = {
|
||||
"device_key": device_key,
|
||||
"title": title,
|
||||
"body": message,
|
||||
"sound": sound,
|
||||
"group": group
|
||||
}
|
||||
|
||||
# 可选参数
|
||||
if icon:
|
||||
data["icon"] = icon
|
||||
if url:
|
||||
data["url"] = url
|
||||
|
||||
logger.info(f"📱 Bark通知 - API地址: {api_url}")
|
||||
logger.info(f"📱 Bark通知 - 请求数据构建完成")
|
||||
|
||||
# 发送POST请求
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(api_url, json=data, timeout=10) as response:
|
||||
response_text = await response.text()
|
||||
logger.info(f"📱 Bark通知 - 响应状态: {response.status}")
|
||||
logger.info(f"📱 Bark通知 - 响应内容: {response_text}")
|
||||
|
||||
if response.status == 200:
|
||||
try:
|
||||
response_json = json.loads(response_text)
|
||||
if response_json.get('code') == 200:
|
||||
logger.info(f"📱 Bark通知发送成功")
|
||||
else:
|
||||
logger.warning(f"📱 Bark通知发送失败: {response_json.get('message', '未知错误')}")
|
||||
except json.JSONDecodeError:
|
||||
# 某些Bark服务器可能返回纯文本
|
||||
if 'success' in response_text.lower() or 'ok' in response_text.lower():
|
||||
logger.info(f"📱 Bark通知发送成功")
|
||||
else:
|
||||
logger.warning(f"📱 Bark通知响应格式异常: {response_text}")
|
||||
else:
|
||||
logger.warning(f"📱 Bark通知发送失败: HTTP {response.status}, 响应: {response_text}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"📱 发送Bark通知异常: {self._safe_str(e)}")
|
||||
import traceback
|
||||
logger.error(f"📱 Bark通知异常详情: {traceback.format_exc()}")
|
||||
|
||||
async def _send_email_notification(self, config_data: dict, message: str):
|
||||
"""发送邮件通知"""
|
||||
try:
|
||||
@ -2218,6 +2377,12 @@ class XianyuLive:
|
||||
case 'ding_talk' | 'dingtalk':
|
||||
await self._send_dingtalk_notification(config_data, notification_msg)
|
||||
notification_sent = True
|
||||
case 'feishu' | 'lark':
|
||||
await self._send_feishu_notification(config_data, notification_msg)
|
||||
notification_sent = True
|
||||
case 'bark':
|
||||
await self._send_bark_notification(config_data, notification_msg)
|
||||
notification_sent = True
|
||||
case 'email':
|
||||
await self._send_email_notification(config_data, notification_msg)
|
||||
notification_sent = True
|
||||
|
@ -1064,6 +1064,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-md-4 col-lg-3 col-xl-2">
|
||||
<div class="card h-100 channel-type-card" onclick="showAddChannelModal('feishu')">
|
||||
<div class="card-body text-center">
|
||||
<div class="channel-icon">
|
||||
<i class="bi bi-chat-square-text-fill text-warning"></i>
|
||||
</div>
|
||||
<h6 class="card-title">飞书通知</h6>
|
||||
<p class="card-text text-muted">飞书机器人消息</p>
|
||||
<div class="mt-auto">
|
||||
<button class="btn btn-outline-warning btn-sm">
|
||||
<i class="bi bi-plus-circle me-1"></i>配置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-md-4 col-lg-3 col-xl-2">
|
||||
<div class="card h-100 channel-type-card" onclick="showAddChannelModal('bark')">
|
||||
<div class="card-body text-center">
|
||||
<div class="channel-icon">
|
||||
<i class="bi bi-phone-fill text-dark"></i>
|
||||
</div>
|
||||
<h6 class="card-title">Bark通知</h6>
|
||||
<p class="card-text text-muted">iOS推送通知</p>
|
||||
<div class="mt-auto">
|
||||
<button class="btn btn-outline-dark btn-sm">
|
||||
<i class="bi bi-plus-circle me-1"></i>配置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-md-4 col-lg-3 col-xl-2">
|
||||
<div class="card h-100 channel-type-card" onclick="showAddChannelModal('email')">
|
||||
<div class="card-body text-center">
|
||||
@ -2720,7 +2754,7 @@
|
||||
|
||||
<!-- JS依赖 -->
|
||||
<script src="/static/lib/bootstrap/bootstrap.bundle.min.js"></script>
|
||||
<script src="/static/js/app.js"></script>
|
||||
<script src="/static/js/app.js?v=2.2.0"></script>
|
||||
|
||||
<!-- 默认回复管理模态框 -->
|
||||
<div class="modal fade" id="defaultReplyModal" tabindex="-1" aria-labelledby="defaultReplyModalLabel" aria-hidden="true">
|
||||
|
@ -2450,6 +2450,78 @@ const channelTypeConfigs = {
|
||||
}
|
||||
]
|
||||
},
|
||||
feishu: {
|
||||
title: '飞书通知',
|
||||
description: '请设置飞书机器人Webhook URL,支持自定义机器人和群机器人',
|
||||
icon: 'bi-chat-square-text-fill',
|
||||
color: 'warning',
|
||||
fields: [
|
||||
{
|
||||
id: 'webhook_url',
|
||||
label: '飞书机器人Webhook URL',
|
||||
type: 'url',
|
||||
placeholder: 'https://open.feishu.cn/open-apis/bot/v2/hook/...',
|
||||
required: true,
|
||||
help: '飞书机器人的Webhook地址'
|
||||
},
|
||||
{
|
||||
id: 'secret',
|
||||
label: '签名密钥(可选)',
|
||||
type: 'text',
|
||||
placeholder: '输入签名密钥',
|
||||
required: false,
|
||||
help: '如果机器人开启了签名验证,请填写密钥'
|
||||
}
|
||||
]
|
||||
},
|
||||
bark: {
|
||||
title: 'Bark通知',
|
||||
description: 'iOS推送通知服务,支持自建服务器和官方服务器',
|
||||
icon: 'bi-phone-fill',
|
||||
color: 'dark',
|
||||
fields: [
|
||||
{
|
||||
id: 'device_key',
|
||||
label: '设备密钥',
|
||||
type: 'text',
|
||||
placeholder: '输入Bark设备密钥',
|
||||
required: true,
|
||||
help: 'Bark应用中显示的设备密钥'
|
||||
},
|
||||
{
|
||||
id: 'server_url',
|
||||
label: '服务器地址(可选)',
|
||||
type: 'url',
|
||||
placeholder: 'https://api.day.app',
|
||||
required: false,
|
||||
help: '自建Bark服务器地址,留空使用官方服务器'
|
||||
},
|
||||
{
|
||||
id: 'title',
|
||||
label: '通知标题(可选)',
|
||||
type: 'text',
|
||||
placeholder: '闲鱼自动回复通知',
|
||||
required: false,
|
||||
help: '推送通知的标题'
|
||||
},
|
||||
{
|
||||
id: 'sound',
|
||||
label: '提示音(可选)',
|
||||
type: 'text',
|
||||
placeholder: 'default',
|
||||
required: false,
|
||||
help: '通知提示音,如:alarm, anticipate, bell等'
|
||||
},
|
||||
{
|
||||
id: 'group',
|
||||
label: '分组(可选)',
|
||||
type: 'text',
|
||||
placeholder: 'xianyu',
|
||||
required: false,
|
||||
help: '通知分组名称,用于归类消息'
|
||||
}
|
||||
]
|
||||
},
|
||||
email: {
|
||||
title: '邮件通知',
|
||||
description: '通过SMTP服务器发送邮件通知,支持各种邮箱服务商',
|
||||
@ -2753,6 +2825,8 @@ function renderNotificationChannels(channels) {
|
||||
let channelType = channel.type;
|
||||
if (channelType === 'ding_talk') {
|
||||
channelType = 'dingtalk'; // 兼容旧的类型名
|
||||
} else if (channelType === 'lark') {
|
||||
channelType = 'feishu'; // 兼容lark类型名
|
||||
}
|
||||
const typeConfig = channelTypeConfigs[channelType];
|
||||
const typeDisplay = typeConfig ? typeConfig.title : channel.type;
|
||||
@ -2867,6 +2941,8 @@ async function editNotificationChannel(channelId) {
|
||||
let channelType = channel.type;
|
||||
if (channelType === 'ding_talk') {
|
||||
channelType = 'dingtalk'; // 兼容旧的类型名
|
||||
} else if (channelType === 'lark') {
|
||||
channelType = 'feishu'; // 兼容lark类型名
|
||||
}
|
||||
|
||||
const config = channelTypeConfigs[channelType];
|
||||
@ -2891,6 +2967,10 @@ async function editNotificationChannel(channelId) {
|
||||
configData = { qq_number: channel.config };
|
||||
} else if (channel.type === 'dingtalk' || channel.type === 'ding_talk') {
|
||||
configData = { webhook_url: channel.config };
|
||||
} else if (channel.type === 'feishu' || channel.type === 'lark') {
|
||||
configData = { webhook_url: channel.config };
|
||||
} else if (channel.type === 'bark') {
|
||||
configData = { device_key: channel.config };
|
||||
} else {
|
||||
configData = { config: channel.config };
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user