diff --git a/Dockerfile b/Dockerfile index 69e2faa..bc7cd66 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,10 +2,12 @@ FROM python:3.11-slim # 设置标签信息 -LABEL maintainer="Xianyu Auto Reply System" +LABEL maintainer="zhinianboke" LABEL version="2.0.0" LABEL description="闲鱼自动回复系统 - 企业级多用户版本" LABEL repository="https://github.com/zhinianboke/xianyu-auto-reply" +LABEL license="仅供学习使用,禁止商业用途" +LABEL author="zhinianboke" # 设置工作目录 WORKDIR /app @@ -20,14 +22,18 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright # 安装系统依赖(包括Playwright浏览器依赖) RUN apt-get update && \ apt-get install -y --no-install-recommends \ + # 基础工具 nodejs \ npm \ tzdata \ curl \ + ca-certificates \ + # 图像处理依赖 libjpeg-dev \ libpng-dev \ libfreetype6-dev \ fonts-dejavu-core \ + fonts-liberation \ # Playwright浏览器依赖 libnss3 \ libnspr4 \ @@ -50,31 +56,15 @@ RUN apt-get update && \ libx11-6 \ libxft2 \ libxinerama1 \ - libxrandr2 \ - libxss1 \ libxtst6 \ - ca-certificates \ - fonts-liberation \ libappindicator3-1 \ - libasound2 \ - libatk-bridge2.0-0 \ - libdrm2 \ - libgtk-3-0 \ - libnspr4 \ - libnss3 \ libx11-xcb1 \ - libxcomposite1 \ - libxcursor1 \ - libxdamage1 \ libxfixes3 \ - libxi6 \ - libxrandr2 \ - libxrender1 \ - libxss1 \ - libxtst6 \ xdg-utils \ && apt-get clean \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /tmp/* \ + && rm -rf /var/tmp/* # 设置时区 RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone @@ -110,18 +100,22 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8080/health || exit 1 # 创建启动脚本 -RUN echo '#!/bin/bash' > /app/entrypoint.sh && \ - echo 'set -e' >> /app/entrypoint.sh && \ - echo '' >> /app/entrypoint.sh && \ - echo 'echo "🚀 启动闲鱼自动回复系统..."' >> /app/entrypoint.sh && \ - echo '' >> /app/entrypoint.sh && \ - echo '# 数据库将在应用启动时自动初始化' >> /app/entrypoint.sh && \ - echo 'echo "📊 数据库将在应用启动时自动初始化..."' >> /app/entrypoint.sh && \ - echo '' >> /app/entrypoint.sh && \ - echo '# 启动主应用' >> /app/entrypoint.sh && \ - echo 'echo "🎯 启动主应用..."' >> /app/entrypoint.sh && \ - echo 'exec python Start.py' >> /app/entrypoint.sh && \ - chmod +x /app/entrypoint.sh +COPY < ⚠️ **安全提示**:首次登录后请立即修改默认密码! + +### 🔧 Docker部署管理 + +使用 `docker-deploy.sh` 脚本可以方便地管理Docker部署: + +```bash +# 查看所有可用命令 +./docker-deploy.sh help + +# 初始化配置 +./docker-deploy.sh init + +# 构建镜像 +./docker-deploy.sh build + +# 启动服务 +./docker-deploy.sh start + +# 启动包含Nginx的完整服务 +./docker-deploy.sh start with-nginx + +# 查看服务状态 +./docker-deploy.sh status + +# 查看实时日志 +./docker-deploy.sh logs + +# 备份数据 +./docker-deploy.sh backup + +# 更新部署 +./docker-deploy.sh update + +# 停止服务 +./docker-deploy.sh stop + +# 重启服务 +./docker-deploy.sh restart + +# 清理环境 +./docker-deploy.sh cleanup +``` + +### 🛠️ 故障排除 + +**常见问题及解决方案**: + +1. **Docker未运行** + ```bash + # 启动Docker Desktop或Docker服务 + sudo systemctl start docker # Linux + ``` + +2. **端口冲突** + ```bash + # 修改.env文件中的WEB_PORT + WEB_PORT=8081 + ``` + +3. **权限问题** + ```bash + # 确保数据目录有正确权限 + sudo chown -R $USER:$USER ./data ./logs ./backups + ``` + +4. **内存不足** + ```bash + # 调整.env文件中的资源限制 + MEMORY_LIMIT=1024 + CPU_LIMIT=1.0 + ``` + +5. **Playwright浏览器安装失败** + ```bash + # 手动安装浏览器 + playwright install chromium --with-deps + ``` - **权限问题**:Linux系统下使用 `sudo ./docker-deploy.sh` ### 方式三:本地部署 @@ -270,6 +388,59 @@ docker rm -f xianyu-auto-reply └─────────────────────────────────────┘ ``` +## ✨ 核心功能特性 + +### 🚀 自动回复系统 +- **智能关键词匹配** - 支持精确匹配和模糊匹配,灵活配置回复规则 +- **AI智能回复** - 集成多种AI模型(通义千问、GPT等),智能理解用户意图 +- **多账号管理** - 支持同时管理多个闲鱼账号,独立配置和运行 +- **实时消息处理** - WebSocket长连接,毫秒级响应用户消息 +- **自定义回复模板** - 支持占位符和动态内容,个性化回复体验 + +### 🛒 自动发货系统 +- **智能订单识别** - 自动识别虚拟商品订单,精准匹配发货规则 +- **多重安全验证** - 超级加密保护,防止误操作和数据泄露 +- **批量处理能力** - 支持批量确认发货,提高处理效率 +- **异常处理机制** - 完善的错误处理和重试机制,确保发货成功 +- **多渠道通知** - 支持QQ、钉钉、邮件等多种发货通知方式 + +### 👥 多用户系统 +- **用户注册登录** - 支持邮箱验证和图形验证码,安全可靠 +- **权限管理** - 管理员和普通用户权限分离,精细化权限控制 +- **数据隔离** - 每个用户的数据完全隔离,保护隐私安全 +- **会话管理** - JWT Token认证,支持自动续期和安全登出 + +### 📊 数据管理 +- **商品信息管理** - 自动获取和同步商品信息,实时更新状态 +- **订单数据统计** - 详细的订单数据分析和可视化图表 +- **关键词管理** - 灵活的关键词配置,支持正则表达式 +- **数据导入导出** - 支持Excel格式的批量数据操作 +- **自动备份** - 定期自动备份重要数据,防止数据丢失 + +### 🔍 商品搜索 +- **真实数据获取** - 基于Playwright技术,获取真实闲鱼商品数据 +- **多页搜索** - 支持分页搜索和批量获取,无限制数据采集 +- **数据可视化** - 美观的商品展示界面,支持排序和筛选 +- **搜索历史** - 保存搜索历史和结果,方便数据分析 + +### 📱 通知系统 +- **多渠道支持** - QQ、钉钉、邮件、微信、Telegram等6种通知方式 +- **智能配置** - 可视化配置界面,支持复杂参数和加密设置 +- **实时推送** - 重要事件实时通知,及时了解系统状态 +- **通知模板** - 自定义通知内容和格式,个性化消息推送 + +### 🔐 安全特性 +- **Cookie安全管理** - 加密存储用户凭证,定期自动刷新 +- **Token自动刷新** - 智能检测和刷新过期Token,保持连接稳定 +- **操作日志** - 详细记录所有操作日志,支持审计和追踪 +- **异常监控** - 实时监控系统异常和错误,主动预警 + +### 🎨 用户界面 +- **现代化设计** - 基于Bootstrap 5的响应式界面,美观易用 +- **多主题支持** - 支持明暗主题切换,个性化界面体验 +- **移动端适配** - 完美适配手机和平板设备,随时随地管理 +- **实时更新** - 界面数据实时更新,无需手动刷新 + ## 📁 核心文件功能说明 ### 🚀 启动和核心模块 diff --git a/XianyuAutoAsync.py b/XianyuAutoAsync.py index 1812fce..0f44fae 100644 --- a/XianyuAutoAsync.py +++ b/XianyuAutoAsync.py @@ -918,11 +918,22 @@ class XianyuLive: channel_config = notification.get('channel_config') try: + # 解析配置数据 + config_data = self._parse_notification_config(channel_config) + 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) + await self._send_qq_notification(config_data, notification_msg) + case 'ding_talk' | 'dingtalk': + await self._send_dingtalk_notification(config_data, notification_msg) + case 'email': + await self._send_email_notification(config_data, notification_msg) + case 'webhook': + await self._send_webhook_notification(config_data, notification_msg) + case 'wechat': + await self._send_wechat_notification(config_data, notification_msg) + case 'telegram': + await self._send_telegram_notification(config_data, notification_msg) case _: logger.warning(f"不支持的通知渠道类型: {channel_type}") @@ -932,13 +943,25 @@ class XianyuLive: except Exception as e: logger.error(f"处理消息通知失败: {self._safe_str(e)}") - async def _send_qq_notification(self, config: str, message: str): + def _parse_notification_config(self, config: str) -> dict: + """解析通知配置数据""" + try: + import json + # 尝试解析JSON格式的配置 + return json.loads(config) + except (json.JSONDecodeError, TypeError): + # 兼容旧格式(直接字符串) + return {"config": config} + + async def _send_qq_notification(self, config_data: dict, message: str): """发送QQ通知""" try: import aiohttp # 解析配置(QQ号码) - qq_number = config.strip() + qq_number = config_data.get('qq_number') or config_data.get('config', '') + qq_number = qq_number.strip() if qq_number else '' + if not qq_number: logger.warning("QQ通知配置为空") return @@ -961,17 +984,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): + async def _send_dingtalk_notification(self, config_data: dict, message: str): """发送钉钉通知""" try: import aiohttp import json - # 解析配置(钉钉机器人Webhook URL) - webhook_url = config.strip() + import hmac + import hashlib + import base64 + import time + + # 解析配置 + webhook_url = config_data.get('webhook_url') or config_data.get('config', '') + secret = config_data.get('secret', '') + + webhook_url = webhook_url.strip() if webhook_url else '' if not webhook_url: logger.warning("钉钉通知配置为空") return + # 如果有加签密钥,生成签名 + if secret: + timestamp = str(round(time.time() * 1000)) + secret_enc = secret.encode('utf-8') + string_to_sign = f'{timestamp}\n{secret}' + string_to_sign_enc = string_to_sign.encode('utf-8') + hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest() + sign = base64.b64encode(hmac_code).decode('utf-8') + webhook_url += f'×tamp={timestamp}&sign={sign}' + data = { "msgtype": "markdown", "markdown": { @@ -983,13 +1024,165 @@ class XianyuLive: 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}") + logger.info(f"钉钉通知发送成功") else: logger.warning(f"钉钉通知发送失败: {response.status}") - + except Exception as e: logger.error(f"发送钉钉通知异常: {self._safe_str(e)}") + async def _send_email_notification(self, config_data: dict, message: str): + """发送邮件通知""" + try: + import smtplib + from email.mime.text import MIMEText + from email.mime.multipart import MIMEMultipart + + # 解析配置 + smtp_server = config_data.get('smtp_server', '') + smtp_port = int(config_data.get('smtp_port', 587)) + email_user = config_data.get('email_user', '') + email_password = config_data.get('email_password', '') + recipient_email = config_data.get('recipient_email', '') + + if not all([smtp_server, email_user, email_password, recipient_email]): + logger.warning("邮件通知配置不完整") + return + + # 创建邮件 + msg = MIMEMultipart() + msg['From'] = email_user + msg['To'] = recipient_email + msg['Subject'] = "闲鱼自动回复通知" + + # 添加邮件正文 + msg.attach(MIMEText(message, 'plain', 'utf-8')) + + # 发送邮件 + server = smtplib.SMTP(smtp_server, smtp_port) + server.starttls() + server.login(email_user, email_password) + server.send_message(msg) + server.quit() + + logger.info(f"邮件通知发送成功: {recipient_email}") + + except Exception as e: + logger.error(f"发送邮件通知异常: {self._safe_str(e)}") + + async def _send_webhook_notification(self, config_data: dict, message: str): + """发送Webhook通知""" + try: + import aiohttp + import json + + # 解析配置 + webhook_url = config_data.get('webhook_url', '') + http_method = config_data.get('http_method', 'POST').upper() + headers_str = config_data.get('headers', '{}') + + if not webhook_url: + logger.warning("Webhook通知配置为空") + return + + # 解析自定义请求头 + try: + custom_headers = json.loads(headers_str) if headers_str else {} + except json.JSONDecodeError: + custom_headers = {} + + # 设置默认请求头 + headers = {'Content-Type': 'application/json'} + headers.update(custom_headers) + + # 构建请求数据 + data = { + 'message': message, + 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'), + 'source': 'xianyu-auto-reply' + } + + async with aiohttp.ClientSession() as session: + if http_method == 'POST': + async with session.post(webhook_url, json=data, headers=headers, timeout=10) as response: + if response.status == 200: + logger.info(f"Webhook通知发送成功") + else: + logger.warning(f"Webhook通知发送失败: {response.status}") + elif http_method == 'PUT': + async with session.put(webhook_url, json=data, headers=headers, timeout=10) as response: + if response.status == 200: + logger.info(f"Webhook通知发送成功") + else: + logger.warning(f"Webhook通知发送失败: {response.status}") + else: + logger.warning(f"不支持的HTTP方法: {http_method}") + + except Exception as e: + logger.error(f"发送Webhook通知异常: {self._safe_str(e)}") + + async def _send_wechat_notification(self, config_data: dict, message: str): + """发送微信通知""" + try: + import aiohttp + import json + + # 解析配置 + webhook_url = config_data.get('webhook_url', '') + + if not webhook_url: + logger.warning("微信通知配置为空") + return + + data = { + "msgtype": "text", + "text": { + "content": 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"微信通知发送成功") + else: + logger.warning(f"微信通知发送失败: {response.status}") + + except Exception as e: + logger.error(f"发送微信通知异常: {self._safe_str(e)}") + + async def _send_telegram_notification(self, config_data: dict, message: str): + """发送Telegram通知""" + try: + import aiohttp + + # 解析配置 + bot_token = config_data.get('bot_token', '') + chat_id = config_data.get('chat_id', '') + + if not all([bot_token, chat_id]): + logger.warning("Telegram通知配置不完整") + return + + # 构建API URL + api_url = f"https://api.telegram.org/bot{bot_token}/sendMessage" + + data = { + 'chat_id': chat_id, + 'text': message, + 'parse_mode': 'HTML' + } + + async with aiohttp.ClientSession() as session: + async with session.post(api_url, json=data, timeout=10) as response: + if response.status == 200: + logger.info(f"Telegram通知发送成功") + else: + logger.warning(f"Telegram通知发送失败: {response.status}") + + except Exception as e: + logger.error(f"发送Telegram通知异常: {self._safe_str(e)}") + async def send_token_refresh_notification(self, error_message: str, notification_type: str = "token_refresh"): """发送Token刷新异常通知(带防重复机制)""" try: @@ -1036,11 +1229,30 @@ class XianyuLive: channel_config = notification.get('channel_config') try: - if channel_type == 'qq': - await self._send_qq_notification(channel_config, notification_msg) - notification_sent = True - else: - logger.warning(f"不支持的通知渠道类型: {channel_type}") + # 解析配置数据 + config_data = self._parse_notification_config(channel_config) + + match channel_type: + case 'qq': + await self._send_qq_notification(config_data, notification_msg) + notification_sent = True + case 'ding_talk' | 'dingtalk': + await self._send_dingtalk_notification(config_data, notification_msg) + notification_sent = True + case 'email': + await self._send_email_notification(config_data, notification_msg) + notification_sent = True + case 'webhook': + await self._send_webhook_notification(config_data, notification_msg) + notification_sent = True + case 'wechat': + await self._send_wechat_notification(config_data, notification_msg) + notification_sent = True + case 'telegram': + await self._send_telegram_notification(config_data, notification_msg) + notification_sent = True + case _: + logger.warning(f"不支持的通知渠道类型: {channel_type}") except Exception as notify_error: logger.error(f"发送Token刷新通知失败 ({notification.get('channel_name', 'Unknown')}): {self._safe_str(notify_error)}") @@ -1106,9 +1318,34 @@ class XianyuLive: channel_type = notification.get('channel_type', 'qq') channel_config = notification.get('channel_config', '') - if channel_type == 'qq': - await self._send_qq_notification(channel_config, notification_message) - logger.info(f"已发送自动发货通知到QQ: {channel_config}") + try: + # 解析配置数据 + config_data = self._parse_notification_config(channel_config) + + match channel_type: + case 'qq': + await self._send_qq_notification(config_data, notification_message) + logger.info(f"已发送自动发货通知到QQ") + case 'ding_talk' | 'dingtalk': + await self._send_dingtalk_notification(config_data, notification_message) + logger.info(f"已发送自动发货通知到钉钉") + case 'email': + await self._send_email_notification(config_data, notification_message) + logger.info(f"已发送自动发货通知到邮箱") + case 'webhook': + await self._send_webhook_notification(config_data, notification_message) + logger.info(f"已发送自动发货通知到Webhook") + case 'wechat': + await self._send_wechat_notification(config_data, notification_message) + logger.info(f"已发送自动发货通知到微信") + case 'telegram': + await self._send_telegram_notification(config_data, notification_message) + logger.info(f"已发送自动发货通知到Telegram") + case _: + logger.warning(f"不支持的通知渠道类型: {channel_type}") + + except Exception as notify_error: + logger.error(f"发送自动发货通知失败: {self._safe_str(notify_error)}") except Exception as e: logger.error(f"发送自动发货通知异常: {self._safe_str(e)}") diff --git a/db_manager.py b/db_manager.py index e437e12..941c554 100644 --- a/db_manager.py +++ b/db_manager.py @@ -371,6 +371,13 @@ class DBManager: self.set_system_setting("db_version", "1.1", "数据库版本号") logger.info("数据库升级到版本1.1完成") + # 升级到版本1.2 - 支持更多通知渠道类型 + if current_version < "1.2": + logger.info("开始升级数据库到版本1.2...") + self.upgrade_notification_channels_types(cursor) + self.set_system_setting("db_version", "1.2", "数据库版本号") + logger.info("数据库升级到版本1.2完成") + except Exception as e: logger.error(f"数据库版本检查或升级失败: {e}") @@ -527,6 +534,70 @@ class DBManager: except Exception as e: logger.error(f"升级notification_channels表失败: {e}") raise + + def upgrade_notification_channels_types(self, cursor): + """升级notification_channels表支持更多渠道类型""" + 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] + + # 获取现有数据 + existing_data = [] + if count > 0: + cursor.execute("SELECT * FROM notification_channels") + existing_data = cursor.fetchall() + logger.info(f"备份 {count} 条通知渠道数据") + + # 创建新表,支持更多渠道类型 + 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','dingtalk','email','webhook','wechat','telegram')), + config TEXT NOT NULL, + enabled BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ''') + + # 复制数据,同时处理类型映射 + if existing_data: + logger.info(f"迁移 {len(existing_data)} 条通知渠道数据到新表") + for row in existing_data: + # 处理类型映射:ding_talk -> dingtalk + channel_type = row[3] # type字段 + if channel_type == 'ding_talk': + channel_type = 'dingtalk' + + # 插入到新表 + cursor.execute(''' + INSERT INTO notification_channels_new + (id, name, user_id, type, config, enabled, created_at, updated_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ''', (row[0], row[1], row[2], channel_type, row[4], row[5], row[6], row[7])) + + # 删除旧表 + 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): """迁移keywords表的约束,支持基于商品ID的唯一性校验""" diff --git a/requirements.txt b/requirements.txt index b914198..686b841 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,68 +2,174 @@ # 闲鱼自动回复系统 - Python依赖包 # ================================ -# 核心Web框架 +# ==================== 核心Web框架 ==================== fastapi>=0.111.0 uvicorn[standard]>=0.29.0 pydantic>=2.7.0 -# 日志记录 +# ==================== 日志记录 ==================== loguru>=0.7.0 -# 网络通信 +# ==================== 网络通信 ==================== websockets>=10.0,<13.0 aiohttp>=3.9.0 requests>=2.31.0 httpx>=0.25.0 -# 配置文件处理 +# ==================== 配置文件处理 ==================== PyYAML>=6.0.0 python-dotenv>=1.0.1 -# JavaScript执行引擎 +# ==================== JavaScript执行引擎 ==================== PyExecJS>=1.5.1 -# 协议缓冲区解析 +# ==================== 协议缓冲区解析 ==================== blackboxprotobuf>=1.0.1 -# 系统监控 +# ==================== 系统监控 ==================== psutil>=5.9.0 -# 文件上传支持 +# ==================== 文件上传支持 ==================== python-multipart>=0.0.6 -# AI回复引擎 +# ==================== AI回复引擎 ==================== openai>=1.65.5 -# 图像处理(验证码生成、二维码生成) +# ==================== 图像处理 ==================== +# 验证码生成、二维码生成 Pillow>=10.0.0 qrcode[pil]>=7.4.2 -# 浏览器自动化(商品搜索、订单详情获取) +# ==================== 浏览器自动化 ==================== +# 商品搜索、订单详情获取 playwright>=1.40.0 -# 加密和安全 +# ==================== 加密和安全 ==================== PyJWT>=2.8.0 passlib[bcrypt]>=1.7.4 cryptography>=41.0.0 -# 时间处理 +# ==================== 时间处理 ==================== python-dateutil>=2.8.2 -# 正则表达式增强 +# ==================== 正则表达式增强 ==================== regex>=2023.10.3 -# Excel文件处理(数据导入导出) +# ==================== Excel文件处理 ==================== +# 数据导入导出功能 pandas>=2.0.0 openpyxl>=3.1.0 -# 邮件发送(用户注册验证) +# ==================== 邮件发送 ==================== +# 用户注册验证 email-validator>=2.0.0 -# 其他工具库 +# ==================== 其他工具库 ==================== typing-extensions>=4.7.0 -# 注意: -# - sqlite3 是Python内置模块,无需安装 -# - smtplib 是Python内置模块,无需安装 -# - email 是Python内置模块,无需安装 \ No newline at end of file +# ==================== 说明 ==================== +# 以下模块是Python内置模块,无需安装: +# - sqlite3 (数据库) +# - smtplib (邮件发送) +# - email (邮件处理) +# - json (JSON处理) +# - base64 (编码解码) +# - hashlib (哈希算法) +# - hmac (消息认证码) +# - time (时间处理) +# - datetime (日期时间) +# - os (操作系统接口) +# - sys (系统相关) +# - re (正则表达式) +# - urllib (URL处理) +# - asyncio (异步编程) +# - threading (多线程) +# - multiprocessing (多进程) +# - pathlib (路径处理) +# - uuid (UUID生成) +# - random (随机数) +# - secrets (安全随机数) +# - traceback (异常追踪) +# - logging (日志记录) +# - collections (集合类型) +# - itertools (迭代工具) +# - functools (函数工具) +# - operator (操作符函数) +# - copy (对象复制) +# - pickle (对象序列化) +# - gzip (压缩) +# - zipfile (ZIP文件) +# - tarfile (TAR文件) +# - shutil (文件操作) +# - tempfile (临时文件) +# - io (输入输出) +# - csv (CSV文件) +# - xml (XML处理) +# - html (HTML处理) +# - http (HTTP客户端/服务器) +# - socket (网络编程) +# - ssl (SSL/TLS) +# - ftplib (FTP客户端) +# - poplib (POP3客户端) +# - imaplib (IMAP客户端) +# - telnetlib (Telnet客户端) +# - subprocess (子进程) +# - signal (信号处理) +# - atexit (退出处理) +# - weakref (弱引用) +# - gc (垃圾回收) +# - inspect (对象检查) +# - ast (抽象语法树) +# - dis (字节码反汇编) +# - keyword (关键字) +# - token (令牌) +# - tokenize (词法分析) +# - parser (语法分析) +# - symbol (符号) +# - code (代码对象) +# - codeop (代码编译) +# - py_compile (Python编译) +# - compileall (批量编译) +# - importlib (导入机制) +# - pkgutil (包工具) +# - modulefinder (模块查找) +# - runpy (运行Python模块) +# - argparse (命令行参数) +# - getopt (命令行选项) +# - optparse (选项解析) +# - configparser (配置文件) +# - fileinput (文件输入) +# - linecache (行缓存) +# - glob (文件名模式匹配) +# - fnmatch (文件名匹配) +# - difflib (差异比较) +# - textwrap (文本包装) +# - string (字符串) +# - struct (二进制数据) +# - codecs (编解码器) +# - unicodedata (Unicode数据) +# - stringprep (字符串预处理) +# - readline (行编辑) +# - rlcompleter (自动补全) +# - pprint (美化打印) +# - reprlib (repr替代) +# - enum (枚举) +# - numbers (数字抽象基类) +# - math (数学函数) +# - cmath (复数数学) +# - decimal (十进制浮点) +# - fractions (分数) +# - statistics (统计函数) +# - array (数组) +# - bisect (二分查找) +# - heapq (堆队列) +# - queue (队列) +# - types (动态类型) +# - contextlib (上下文管理) +# - abc (抽象基类) +# - atexit (退出处理) +# - traceback (异常追踪) +# - __future__ (未来特性) +# - warnings (警告) +# - dataclasses (数据类) +# - typing (类型提示) \ No newline at end of file diff --git a/static/index.html b/static/index.html index 867ca11..3295df5 100644 --- a/static/index.html +++ b/static/index.html @@ -1189,6 +1189,57 @@ } } + /* 通知渠道卡片样式 */ + .channel-type-card { + cursor: pointer; + transition: all 0.3s ease; + border: 2px solid transparent; + min-height: 180px; /* 设置最小高度,让卡片更紧凑 */ + } + + .channel-type-card .card-body { + padding: 1rem; /* 减少内边距 */ + } + + .channel-type-card .channel-icon { + transition: transform 0.3s ease; + margin-bottom: 0.75rem !important; /* 减少图标下方间距 */ + } + + .channel-type-card .channel-icon i { + font-size: 2.2rem !important; /* 减小图标大小 */ + } + + .channel-type-card .card-title { + font-size: 1.1rem; /* 减小标题字体 */ + margin-bottom: 0.5rem; + } + + .channel-type-card .card-text { + font-size: 0.85rem; /* 减小描述文字 */ + margin-bottom: 0.75rem; + } + + .channel-type-card .btn { + transition: all 0.3s ease; + font-size: 0.85rem; /* 减小按钮文字 */ + padding: 0.375rem 0.75rem; /* 减小按钮内边距 */ + } + + .channel-type-card:hover { + transform: translateY(-3px); /* 减少悬停位移 */ + box-shadow: 0 6px 20px rgba(0,0,0,0.12); + border-color: var(--bs-primary); + } + + .channel-type-card:hover .channel-icon { + transform: scale(1.05); /* 减少悬停缩放 */ + } + + .channel-type-card:hover .btn { + transform: scale(1.02); /* 减少按钮悬停缩放 */ + } + /* 响应式调整 */ @media (max-width: 768px) { .qr-login-btn, .manual-input-btn { @@ -1205,6 +1256,34 @@ height: 25px !important; font-size: 12px; } + + .channel-type-card { + margin-bottom: 10px; + min-height: 160px; /* 移动端稍微减小高度 */ + } + + .channel-type-card .card-body { + padding: 0.75rem; /* 移动端减少内边距 */ + } + + .channel-type-card .channel-icon i { + font-size: 2rem !important; /* 移动端图标更小 */ + } + + .channel-type-card .card-title { + font-size: 1rem; /* 移动端标题更小 */ + } + + .channel-type-card .card-text { + font-size: 0.8rem; /* 移动端描述文字更小 */ + } + } + + /* 大屏幕优化 */ + @media (min-width: 1400px) { + .channel-type-card { + min-height: 200px; /* 大屏幕稍微增加高度 */ + } } @@ -1858,58 +1937,121 @@

管理消息通知渠道,支持QQ通知等多种方式

- +
- 添加QQ通知渠道 + 添加通知渠道
-
+
- 使用说明:需要添加QQ号 3668943488 为好友才能正常接收消息通知 + 选择通知方式:点击下方按钮选择您要配置的通知渠道类型
-
-
- - -
-
- - -
-
- -
-
-
-
-
-
- 添加钉钉通知渠道 -
-
-
- - 使用说明:请设置钉钉机器人Webhook URL + +
+
+
+
+
+ +
+
QQ通知
+

QQ机器人消息

+
+ +
+
+
+
+ +
+
+
+
+ +
+
钉钉通知
+

钉钉机器人消息

+
+ +
+
+
+
+ +
+
+
+
+ +
+
邮件通知
+

SMTP邮件发送

+
+ +
+
+
+
+ +
+
+
+
+ +
+
Webhook
+

自定义HTTP请求

+
+ +
+
+
+
+ +
+
+
+
+ +
+
微信通知
+

企业微信机器人

+
+ +
+
+
+
+ +
+
+
+
+ +
+
Telegram
+

Telegram机器人

+
+ +
+
+
+
-
-
- - -
-
- - -
-
- -
-
@@ -2306,9 +2448,65 @@
+ + +