mirror of
https://github.com/zhinianboke/xianyu-auto-reply.git
synced 2025-08-23 22:37:36 +08:00
Merge pull request #13 from Amazingzl/main
新增钉钉通知渠道支持,更新数据库结构以适应新功能,并优化相关日志记录
This commit is contained in:
commit
2a0bb2368a
@ -918,10 +918,13 @@ class XianyuLive:
|
||||
channel_config = notification.get('channel_config')
|
||||
|
||||
try:
|
||||
if channel_type == 'qq':
|
||||
await self._send_qq_notification(channel_config, notification_msg)
|
||||
else:
|
||||
logger.warning(f"不支持的通知渠道类型: {channel_type}")
|
||||
match channel_type:
|
||||
case 'qq':
|
||||
await self._send_qq_notification(channel_config, notification_msg)
|
||||
case 'ding_talk':
|
||||
await self._send_ding_talk_notification(channel_config, notification_msg)
|
||||
case _:
|
||||
logger.warning(f"不支持的通知渠道类型: {channel_type}")
|
||||
|
||||
except Exception as notify_error:
|
||||
logger.error(f"发送通知失败 ({notification.get('channel_name', 'Unknown')}): {self._safe_str(notify_error)}")
|
||||
@ -958,6 +961,35 @@ class XianyuLive:
|
||||
except Exception as e:
|
||||
logger.error(f"发送QQ通知异常: {self._safe_str(e)}")
|
||||
|
||||
async def _send_ding_talk_notification(self, config: str, message: str):
|
||||
"""发送钉钉通知"""
|
||||
try:
|
||||
import aiohttp
|
||||
import json
|
||||
# 解析配置(钉钉机器人Webhook URL)
|
||||
webhook_url = config.strip()
|
||||
if not webhook_url:
|
||||
logger.warning("钉钉通知配置为空")
|
||||
return
|
||||
|
||||
data = {
|
||||
"msgtype": "markdown",
|
||||
"markdown": {
|
||||
"title": "闲鱼自动回复通知",
|
||||
"text": message
|
||||
}
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(webhook_url, json=data, timeout=10) as response:
|
||||
if response.status == 200:
|
||||
logger.info(f"钉钉通知发送成功: {webhook_url}")
|
||||
else:
|
||||
logger.warning(f"钉钉通知发送失败: {response.status}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"发送钉钉通知异常: {self._safe_str(e)}")
|
||||
|
||||
async def send_token_refresh_notification(self, error_message: str, notification_type: str = "token_refresh"):
|
||||
"""发送Token刷新异常通知(带防重复机制)"""
|
||||
try:
|
||||
|
111
db_manager.py
111
db_manager.py
@ -295,6 +295,16 @@ class DBManager:
|
||||
)
|
||||
''')
|
||||
|
||||
# 创建系统设置表
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS system_settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
description TEXT,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
|
||||
# 创建消息通知配置表
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS message_notifications (
|
||||
@ -310,16 +320,6 @@ class DBManager:
|
||||
)
|
||||
''')
|
||||
|
||||
# 创建系统设置表
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS system_settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL,
|
||||
description TEXT,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
|
||||
# 创建用户设置表
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS user_settings (
|
||||
@ -341,6 +341,45 @@ class DBManager:
|
||||
('theme_color', 'blue', '主题颜色')
|
||||
''')
|
||||
|
||||
# 检查并升级数据库
|
||||
self.check_and_upgrade_db(cursor)
|
||||
|
||||
self.conn.commit()
|
||||
logger.info("数据库初始化完成")
|
||||
except Exception as e:
|
||||
logger.error(f"数据库初始化失败: {e}")
|
||||
self.conn.rollback()
|
||||
raise
|
||||
|
||||
def check_and_upgrade_db(self, cursor):
|
||||
"""检查数据库版本并执行必要的升级"""
|
||||
try:
|
||||
# 获取当前数据库版本
|
||||
current_version = self.get_system_setting("db_version") or "1.0"
|
||||
logger.info(f"当前数据库版本: {current_version}")
|
||||
|
||||
if current_version == "1.0":
|
||||
logger.info("开始升级数据库到版本1.0...")
|
||||
self.update_admin_user_id(cursor)
|
||||
self.set_system_setting("db_version", "1.0", "数据库版本号")
|
||||
logger.info("数据库升级到版本1.0完成")
|
||||
|
||||
# 如果版本低于需要升级的版本,执行升级
|
||||
if current_version < "1.1":
|
||||
logger.info("开始升级数据库到版本1.1...")
|
||||
self.upgrade_notification_channels_table(cursor)
|
||||
self.set_system_setting("db_version", "1.1", "数据库版本号")
|
||||
logger.info("数据库升级到版本1.1完成")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"数据库版本检查或升级失败: {e}")
|
||||
raise
|
||||
|
||||
def update_admin_user_id(self, cursor):
|
||||
"""更新admin用户ID"""
|
||||
try:
|
||||
logger.info("开始更新admin用户ID...")
|
||||
# 创建默认admin用户(只在首次初始化时创建)
|
||||
cursor.execute('SELECT COUNT(*) FROM users WHERE username = ?', ('admin',))
|
||||
admin_exists = cursor.fetchone()[0] > 0
|
||||
@ -438,11 +477,55 @@ class DBManager:
|
||||
self._migrate_keywords_table_constraints(cursor)
|
||||
|
||||
self.conn.commit()
|
||||
logger.info(f"数据库初始化成功: {self.db_path}")
|
||||
logger.info(f"admin用户ID更新完成")
|
||||
except Exception as e:
|
||||
logger.error(f"数据库初始化失败: {e}")
|
||||
if self.conn:
|
||||
self.conn.close()
|
||||
logger.error(f"更新admin用户ID失败: {e}")
|
||||
raise
|
||||
|
||||
def upgrade_notification_channels_table(self, cursor):
|
||||
"""升级notification_channels表的type字段约束"""
|
||||
try:
|
||||
logger.info("开始升级notification_channels表...")
|
||||
|
||||
# 检查表是否存在
|
||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='notification_channels'")
|
||||
if not cursor.fetchone():
|
||||
logger.info("notification_channels表不存在,无需升级")
|
||||
return True
|
||||
|
||||
# 检查表中是否有数据
|
||||
cursor.execute("SELECT COUNT(*) FROM notification_channels")
|
||||
count = cursor.fetchone()[0]
|
||||
|
||||
# 创建临时表
|
||||
cursor.execute('''
|
||||
CREATE TABLE notification_channels_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
type TEXT NOT NULL CHECK (type IN ('qq','ding_talk')),
|
||||
config TEXT NOT NULL,
|
||||
enabled BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
''')
|
||||
|
||||
# 复制数据
|
||||
if count > 0:
|
||||
logger.info(f"复制 {count} 条通知渠道数据到新表")
|
||||
cursor.execute("INSERT INTO notification_channels_new SELECT * FROM notification_channels")
|
||||
|
||||
# 删除旧表
|
||||
cursor.execute("DROP TABLE notification_channels")
|
||||
|
||||
# 重命名新表
|
||||
cursor.execute("ALTER TABLE notification_channels_new RENAME TO notification_channels")
|
||||
|
||||
logger.info("notification_channels表升级完成")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"升级notification_channels表失败: {e}")
|
||||
raise
|
||||
|
||||
def _migrate_keywords_table_constraints(self, cursor):
|
||||
|
@ -1886,6 +1886,33 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<span><i class="bi bi-plus-circle me-2"></i>添加钉钉通知渠道</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle me-2"></i>
|
||||
<strong>使用说明:</strong>请设置钉钉机器人Webhook URL
|
||||
</div>
|
||||
<form id="addDingTalkChannelForm" class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label for="dingTalkChannelName" class="form-label">渠道名称</label>
|
||||
<input type="text" class="form-control" id="dingTalkChannelName" placeholder="例如:我的钉钉通知" required>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="dingTalkWebHook" class="form-label">钉钉机器人Webhook URL</label>
|
||||
<input type="text" class="form-control" id="dingTalkWebHook" placeholder="输入钉钉机器人Webhook URL" required>
|
||||
</div>
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-plus-lg me-1"></i>添加渠道
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 通知渠道列表 -->
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
@ -2298,8 +2325,8 @@
|
||||
<input type="text" class="form-control" id="editChannelName" placeholder="例如:我的QQ通知" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="editChannelQQ" class="form-label">接收QQ号码 <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="editChannelQQ" placeholder="输入QQ号码" required>
|
||||
<label for="editChannelQQ" class="form-label">接收QQ号码、钉钉Webhook URL <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="editChannelQQ" placeholder="输入通知方式" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
@ -4882,7 +4909,7 @@
|
||||
<td colspan="6" class="text-center py-4 text-muted">
|
||||
<i class="bi bi-bell fs-1 d-block mb-3"></i>
|
||||
<h5>暂无通知渠道</h5>
|
||||
<p class="mb-0">请添加QQ通知渠道</p>
|
||||
<p class="mb-0">请添加通知渠道</p>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
@ -4899,7 +4926,7 @@
|
||||
tr.innerHTML = `
|
||||
<td><strong class="text-primary">${channel.id}</strong></td>
|
||||
<td>${channel.name}</td>
|
||||
<td><span class="badge bg-info">QQ</span></td>
|
||||
<td><span class="badge bg-info">${channel.type}</span></td>
|
||||
<td><code>${channel.config}</code></td>
|
||||
<td>${statusBadge}</td>
|
||||
<td>
|
||||
@ -4956,6 +4983,43 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const addDingTalkChannelForm = document.getElementById('addDingTalkChannelForm');
|
||||
if (addDingTalkChannelForm) {
|
||||
addDingTalkChannelForm.addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const name = document.getElementById('dingTalkChannelName').value;
|
||||
const dingTalkWebHook = document.getElementById('dingTalkWebHook').value;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${apiBase}/notification-channels`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${authToken}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: name,
|
||||
type: 'ding_talk',
|
||||
config: dingTalkWebHook
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
showToast('通知渠道添加成功', 'success');
|
||||
addDingTalkChannelForm.reset();
|
||||
loadNotificationChannels();
|
||||
} else {
|
||||
const error = await response.text();
|
||||
showToast(`添加失败: ${error}`, 'danger');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('添加通知渠道失败:', error);
|
||||
showToast('添加通知渠道失败', 'danger');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 删除通知渠道
|
||||
|
Loading…
x
Reference in New Issue
Block a user