mirror of
https://github.com/zhinianboke/xianyu-auto-reply.git
synced 2025-08-29 17:17:38 +08:00
修改扫码登录
This commit is contained in:
parent
4c860980fb
commit
e00e7d09a1
@ -202,6 +202,10 @@ class XianyuLive:
|
|||||||
self.cookie_refresh_running = False # 防止重复执行Cookie刷新
|
self.cookie_refresh_running = False # 防止重复执行Cookie刷新
|
||||||
self.cookie_refresh_enabled = True # 是否启用Cookie刷新功能
|
self.cookie_refresh_enabled = True # 是否启用Cookie刷新功能
|
||||||
|
|
||||||
|
# 扫码登录Cookie刷新标志
|
||||||
|
self.last_qr_cookie_refresh_time = 0 # 记录上次扫码登录Cookie刷新时间
|
||||||
|
self.qr_cookie_refresh_cooldown = 600 # 扫码登录Cookie刷新后的冷却时间:10分钟
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# WebSocket连接监控
|
# WebSocket连接监控
|
||||||
@ -3485,6 +3489,7 @@ class XianyuLive:
|
|||||||
logger.error(f"【{self.cookie_id}】清理任务失败: {self._safe_str(e)}")
|
logger.error(f"【{self.cookie_id}】清理任务失败: {self._safe_str(e)}")
|
||||||
await asyncio.sleep(300) # 出错后也等待5分钟再重试
|
await asyncio.sleep(300) # 出错后也等待5分钟再重试
|
||||||
|
|
||||||
|
|
||||||
async def cookie_refresh_loop(self):
|
async def cookie_refresh_loop(self):
|
||||||
"""Cookie刷新定时任务 - 每小时执行一次"""
|
"""Cookie刷新定时任务 - 每小时执行一次"""
|
||||||
while True:
|
while True:
|
||||||
@ -3578,6 +3583,347 @@ class XianyuLive:
|
|||||||
status = "启用" if enabled else "禁用"
|
status = "启用" if enabled else "禁用"
|
||||||
logger.info(f"【{self.cookie_id}】Cookie刷新功能已{status}")
|
logger.info(f"【{self.cookie_id}】Cookie刷新功能已{status}")
|
||||||
|
|
||||||
|
|
||||||
|
async def refresh_cookies_from_qr_login(self, qr_cookies_str: str, cookie_id: str = None, user_id: int = None):
|
||||||
|
"""使用扫码登录获取的cookie访问指定界面获取真实cookie并存入数据库
|
||||||
|
|
||||||
|
Args:
|
||||||
|
qr_cookies_str: 扫码登录获取的cookie字符串
|
||||||
|
cookie_id: 可选的cookie ID,如果不提供则使用当前实例的cookie_id
|
||||||
|
user_id: 可选的用户ID,如果不提供则使用当前实例的user_id
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 成功返回True,失败返回False
|
||||||
|
"""
|
||||||
|
playwright = None
|
||||||
|
browser = None
|
||||||
|
target_cookie_id = cookie_id or self.cookie_id
|
||||||
|
target_user_id = user_id or self.user_id
|
||||||
|
|
||||||
|
try:
|
||||||
|
import asyncio
|
||||||
|
from playwright.async_api import async_playwright
|
||||||
|
from utils.xianyu_utils import trans_cookies
|
||||||
|
|
||||||
|
logger.info(f"【{target_cookie_id}】开始使用扫码登录cookie获取真实cookie...")
|
||||||
|
logger.info(f"【{target_cookie_id}】扫码cookie长度: {len(qr_cookies_str)}")
|
||||||
|
|
||||||
|
# 解析扫码登录的cookie
|
||||||
|
qr_cookies_dict = trans_cookies(qr_cookies_str)
|
||||||
|
logger.info(f"【{target_cookie_id}】扫码cookie字段数: {len(qr_cookies_dict)}")
|
||||||
|
|
||||||
|
# Docker环境下修复asyncio子进程问题
|
||||||
|
is_docker = os.getenv('DOCKER_ENV') or os.path.exists('/.dockerenv')
|
||||||
|
|
||||||
|
if is_docker:
|
||||||
|
logger.debug(f"【{target_cookie_id}】检测到Docker环境,应用asyncio修复")
|
||||||
|
|
||||||
|
# 创建一个完整的虚拟子进程监视器
|
||||||
|
class DummyChildWatcher:
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
def __exit__(self, *args):
|
||||||
|
pass
|
||||||
|
def is_active(self):
|
||||||
|
return True
|
||||||
|
def add_child_handler(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
def remove_child_handler(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
def attach_loop(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
def __del__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 创建自定义事件循环策略
|
||||||
|
class DockerEventLoopPolicy(asyncio.DefaultEventLoopPolicy):
|
||||||
|
def get_child_watcher(self):
|
||||||
|
return DummyChildWatcher()
|
||||||
|
|
||||||
|
# 临时设置策略
|
||||||
|
old_policy = asyncio.get_event_loop_policy()
|
||||||
|
asyncio.set_event_loop_policy(DockerEventLoopPolicy())
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 添加超时机制,避免无限等待
|
||||||
|
playwright = await asyncio.wait_for(
|
||||||
|
async_playwright().start(),
|
||||||
|
timeout=30.0 # 30秒超时
|
||||||
|
)
|
||||||
|
logger.debug(f"【{target_cookie_id}】Docker环境下Playwright启动成功")
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logger.error(f"【{target_cookie_id}】Docker环境下Playwright启动超时")
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
# 恢复原策略
|
||||||
|
asyncio.set_event_loop_policy(old_policy)
|
||||||
|
else:
|
||||||
|
# 非Docker环境,正常启动(也添加超时保护)
|
||||||
|
try:
|
||||||
|
playwright = await asyncio.wait_for(
|
||||||
|
async_playwright().start(),
|
||||||
|
timeout=30.0 # 30秒超时
|
||||||
|
)
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
logger.error(f"【{target_cookie_id}】Playwright启动超时")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 启动浏览器(参照商品搜索的配置)
|
||||||
|
browser_args = [
|
||||||
|
'--no-sandbox',
|
||||||
|
'--disable-setuid-sandbox',
|
||||||
|
'--disable-dev-shm-usage',
|
||||||
|
'--disable-accelerated-2d-canvas',
|
||||||
|
'--no-first-run',
|
||||||
|
'--no-zygote',
|
||||||
|
'--disable-gpu',
|
||||||
|
'--disable-background-timer-throttling',
|
||||||
|
'--disable-backgrounding-occluded-windows',
|
||||||
|
'--disable-renderer-backgrounding',
|
||||||
|
'--disable-features=TranslateUI',
|
||||||
|
'--disable-ipc-flooding-protection',
|
||||||
|
'--disable-extensions',
|
||||||
|
'--disable-default-apps',
|
||||||
|
'--disable-sync',
|
||||||
|
'--disable-translate',
|
||||||
|
'--hide-scrollbars',
|
||||||
|
'--mute-audio',
|
||||||
|
'--no-default-browser-check',
|
||||||
|
'--no-pings'
|
||||||
|
]
|
||||||
|
|
||||||
|
# 在Docker环境中添加额外参数
|
||||||
|
if os.getenv('DOCKER_ENV'):
|
||||||
|
browser_args.extend([
|
||||||
|
'--single-process',
|
||||||
|
'--disable-background-networking',
|
||||||
|
'--disable-client-side-phishing-detection',
|
||||||
|
'--disable-hang-monitor',
|
||||||
|
'--disable-popup-blocking',
|
||||||
|
'--disable-prompt-on-repost',
|
||||||
|
'--disable-web-resources',
|
||||||
|
'--metrics-recording-only',
|
||||||
|
'--safebrowsing-disable-auto-update',
|
||||||
|
'--enable-automation',
|
||||||
|
'--password-store=basic',
|
||||||
|
'--use-mock-keychain'
|
||||||
|
])
|
||||||
|
|
||||||
|
# 使用无头浏览器
|
||||||
|
browser = await playwright.chromium.launch(
|
||||||
|
headless=True, # 改回无头模式
|
||||||
|
args=browser_args
|
||||||
|
)
|
||||||
|
|
||||||
|
# 创建浏览器上下文
|
||||||
|
context_options = {
|
||||||
|
'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 使用标准窗口大小
|
||||||
|
context_options['viewport'] = {'width': 1920, 'height': 1080}
|
||||||
|
|
||||||
|
context = await browser.new_context(**context_options)
|
||||||
|
|
||||||
|
# 设置扫码登录获取的Cookie
|
||||||
|
cookies = []
|
||||||
|
for cookie_pair in qr_cookies_str.split('; '):
|
||||||
|
if '=' in cookie_pair:
|
||||||
|
name, value = cookie_pair.split('=', 1)
|
||||||
|
cookies.append({
|
||||||
|
'name': name.strip(),
|
||||||
|
'value': value.strip(),
|
||||||
|
'domain': '.goofish.com',
|
||||||
|
'path': '/'
|
||||||
|
})
|
||||||
|
|
||||||
|
await context.add_cookies(cookies)
|
||||||
|
logger.info(f"【{target_cookie_id}】已设置 {len(cookies)} 个扫码Cookie到浏览器")
|
||||||
|
|
||||||
|
# 打印设置的扫码Cookie详情
|
||||||
|
logger.info(f"【{target_cookie_id}】=== 设置到浏览器的扫码Cookie ===")
|
||||||
|
for i, cookie in enumerate(cookies, 1):
|
||||||
|
logger.info(f"【{target_cookie_id}】{i:2d}. {cookie['name']}: {cookie['value'][:50]}{'...' if len(cookie['value']) > 50 else ''}")
|
||||||
|
|
||||||
|
# 创建页面
|
||||||
|
page = await context.new_page()
|
||||||
|
|
||||||
|
# 等待页面准备
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
|
# 访问指定页面获取真实cookie
|
||||||
|
target_url = "https://www.goofish.com/im?spm=a21ybx.home.sidebar.1.4c053da6vYwnmf"
|
||||||
|
logger.info(f"【{target_cookie_id}】访问页面获取真实cookie: {target_url}")
|
||||||
|
|
||||||
|
# 使用更灵活的页面访问策略
|
||||||
|
try:
|
||||||
|
# 首先尝试较短超时
|
||||||
|
await page.goto(target_url, wait_until='domcontentloaded', timeout=15000)
|
||||||
|
logger.info(f"【{target_cookie_id}】页面访问成功")
|
||||||
|
except Exception as e:
|
||||||
|
if 'timeout' in str(e).lower():
|
||||||
|
logger.warning(f"【{target_cookie_id}】页面访问超时,尝试降级策略...")
|
||||||
|
try:
|
||||||
|
# 降级策略:只等待基本加载
|
||||||
|
await page.goto(target_url, wait_until='load', timeout=20000)
|
||||||
|
logger.info(f"【{target_cookie_id}】页面访问成功(降级策略)")
|
||||||
|
except Exception as e2:
|
||||||
|
logger.warning(f"【{target_cookie_id}】降级策略也失败,尝试最基本访问...")
|
||||||
|
# 最后尝试:不等待任何加载完成
|
||||||
|
await page.goto(target_url, timeout=25000)
|
||||||
|
logger.info(f"【{target_cookie_id}】页面访问成功(最基本策略)")
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
# 等待页面完全加载并获取真实cookie
|
||||||
|
logger.info(f"【{target_cookie_id}】页面加载完成,等待获取真实cookie...")
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
# 执行一次刷新以确保获取最新的cookie
|
||||||
|
logger.info(f"【{target_cookie_id}】执行页面刷新获取最新cookie...")
|
||||||
|
try:
|
||||||
|
await page.reload(wait_until='domcontentloaded', timeout=12000)
|
||||||
|
logger.info(f"【{target_cookie_id}】页面刷新成功")
|
||||||
|
except Exception as e:
|
||||||
|
if 'timeout' in str(e).lower():
|
||||||
|
logger.warning(f"【{target_cookie_id}】页面刷新超时,使用降级策略...")
|
||||||
|
await page.reload(wait_until='load', timeout=15000)
|
||||||
|
logger.info(f"【{target_cookie_id}】页面刷新成功(降级策略)")
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
# 获取更新后的真实Cookie
|
||||||
|
logger.info(f"【{target_cookie_id}】获取真实Cookie...")
|
||||||
|
updated_cookies = await context.cookies()
|
||||||
|
|
||||||
|
# 构造新的Cookie字典
|
||||||
|
real_cookies_dict = {}
|
||||||
|
for cookie in updated_cookies:
|
||||||
|
real_cookies_dict[cookie['name']] = cookie['value']
|
||||||
|
|
||||||
|
# 生成真实cookie字符串
|
||||||
|
real_cookies_str = '; '.join([f"{k}={v}" for k, v in real_cookies_dict.items()])
|
||||||
|
|
||||||
|
logger.info(f"【{target_cookie_id}】真实Cookie已获取,包含 {len(real_cookies_dict)} 个字段")
|
||||||
|
|
||||||
|
# 打印完整的真实Cookie内容
|
||||||
|
logger.info(f"【{target_cookie_id}】=== 完整真实Cookie内容 ===")
|
||||||
|
logger.info(f"【{target_cookie_id}】Cookie字符串长度: {len(real_cookies_str)}")
|
||||||
|
logger.info(f"【{target_cookie_id}】Cookie完整内容:")
|
||||||
|
logger.info(f"【{target_cookie_id}】{real_cookies_str}")
|
||||||
|
|
||||||
|
# 打印所有Cookie字段的详细信息
|
||||||
|
logger.info(f"【{target_cookie_id}】=== Cookie字段详细信息 ===")
|
||||||
|
for i, (name, value) in enumerate(real_cookies_dict.items(), 1):
|
||||||
|
# 对于长值,显示前后部分
|
||||||
|
if len(value) > 50:
|
||||||
|
display_value = f"{value[:20]}...{value[-20:]}"
|
||||||
|
else:
|
||||||
|
display_value = value
|
||||||
|
logger.info(f"【{target_cookie_id}】{i:2d}. {name}: {display_value}")
|
||||||
|
|
||||||
|
# 打印原始扫码Cookie对比
|
||||||
|
logger.info(f"【{target_cookie_id}】=== 扫码Cookie对比 ===")
|
||||||
|
logger.info(f"【{target_cookie_id}】扫码Cookie长度: {len(qr_cookies_str)}")
|
||||||
|
logger.info(f"【{target_cookie_id}】扫码Cookie字段数: {len(qr_cookies_dict)}")
|
||||||
|
logger.info(f"【{target_cookie_id}】真实Cookie长度: {len(real_cookies_str)}")
|
||||||
|
logger.info(f"【{target_cookie_id}】真实Cookie字段数: {len(real_cookies_dict)}")
|
||||||
|
logger.info(f"【{target_cookie_id}】长度增加: {len(real_cookies_str) - len(qr_cookies_str)} 字符")
|
||||||
|
logger.info(f"【{target_cookie_id}】字段增加: {len(real_cookies_dict) - len(qr_cookies_dict)} 个")
|
||||||
|
|
||||||
|
# 检查Cookie变化
|
||||||
|
changed_cookies = []
|
||||||
|
new_cookies = []
|
||||||
|
for name, new_value in real_cookies_dict.items():
|
||||||
|
old_value = qr_cookies_dict.get(name)
|
||||||
|
if old_value is None:
|
||||||
|
new_cookies.append(name)
|
||||||
|
elif old_value != new_value:
|
||||||
|
changed_cookies.append(name)
|
||||||
|
|
||||||
|
# 显示Cookie变化统计
|
||||||
|
if changed_cookies:
|
||||||
|
logger.info(f"【{target_cookie_id}】发生变化的Cookie字段 ({len(changed_cookies)}个): {', '.join(changed_cookies)}")
|
||||||
|
if new_cookies:
|
||||||
|
logger.info(f"【{target_cookie_id}】新增的Cookie字段 ({len(new_cookies)}个): {', '.join(new_cookies)}")
|
||||||
|
if not changed_cookies and not new_cookies:
|
||||||
|
logger.info(f"【{target_cookie_id}】Cookie无变化")
|
||||||
|
|
||||||
|
# 打印重要Cookie字段的完整详情
|
||||||
|
important_cookies = ['_m_h5_tk', '_m_h5_tk_enc', 'cookie2', 't', 'sgcookie', 'unb', 'uc1', 'uc3', 'uc4']
|
||||||
|
logger.info(f"【{target_cookie_id}】=== 重要Cookie字段完整详情 ===")
|
||||||
|
for cookie_name in important_cookies:
|
||||||
|
if cookie_name in real_cookies_dict:
|
||||||
|
cookie_value = real_cookies_dict[cookie_name]
|
||||||
|
|
||||||
|
# 标记是否发生了变化
|
||||||
|
change_mark = " [已变化]" if cookie_name in changed_cookies else " [新增]" if cookie_name in new_cookies else " [无变化]"
|
||||||
|
|
||||||
|
# 显示完整的cookie值
|
||||||
|
logger.info(f"【{target_cookie_id}】{cookie_name}{change_mark}:")
|
||||||
|
logger.info(f"【{target_cookie_id}】 值: {cookie_value}")
|
||||||
|
logger.info(f"【{target_cookie_id}】 长度: {len(cookie_value)}")
|
||||||
|
|
||||||
|
# 如果有对应的扫码cookie值,显示对比
|
||||||
|
if cookie_name in qr_cookies_dict:
|
||||||
|
old_value = qr_cookies_dict[cookie_name]
|
||||||
|
if old_value != cookie_value:
|
||||||
|
logger.info(f"【{target_cookie_id}】 原值: {old_value}")
|
||||||
|
logger.info(f"【{target_cookie_id}】 原长度: {len(old_value)}")
|
||||||
|
logger.info(f"【{target_cookie_id}】 ---")
|
||||||
|
else:
|
||||||
|
logger.info(f"【{target_cookie_id}】{cookie_name}: [不存在]")
|
||||||
|
|
||||||
|
# 保存真实Cookie到数据库
|
||||||
|
from db_manager import db_manager
|
||||||
|
success = db_manager.save_cookie(target_cookie_id, real_cookies_str, target_user_id)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
logger.info(f"【{target_cookie_id}】真实Cookie已成功保存到数据库")
|
||||||
|
|
||||||
|
# 如果当前实例的cookie_id匹配,更新实例的cookie信息
|
||||||
|
if target_cookie_id == self.cookie_id:
|
||||||
|
self.cookies = real_cookies_dict
|
||||||
|
self.cookies_str = real_cookies_str
|
||||||
|
logger.info(f"【{target_cookie_id}】已更新当前实例的Cookie信息")
|
||||||
|
|
||||||
|
# 更新扫码登录Cookie刷新时间标志
|
||||||
|
self.last_qr_cookie_refresh_time = time.time()
|
||||||
|
logger.info(f"【{target_cookie_id}】已更新扫码登录Cookie刷新时间标志,_refresh_cookies_via_browser将等待{self.qr_cookie_refresh_cooldown//60}分钟后执行")
|
||||||
|
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.error(f"【{target_cookie_id}】保存真实Cookie到数据库失败")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"【{target_cookie_id}】使用扫码cookie获取真实cookie失败: {self._safe_str(e)}")
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
# 确保资源清理
|
||||||
|
try:
|
||||||
|
if browser:
|
||||||
|
await browser.close()
|
||||||
|
if playwright:
|
||||||
|
await playwright.stop()
|
||||||
|
except Exception as cleanup_e:
|
||||||
|
logger.warning(f"【{target_cookie_id}】清理浏览器资源时出错: {self._safe_str(cleanup_e)}")
|
||||||
|
|
||||||
|
def reset_qr_cookie_refresh_flag(self):
|
||||||
|
"""重置扫码登录Cookie刷新标志,允许立即执行_refresh_cookies_via_browser"""
|
||||||
|
self.last_qr_cookie_refresh_time = 0
|
||||||
|
logger.info(f"【{self.cookie_id}】已重置扫码登录Cookie刷新标志")
|
||||||
|
|
||||||
|
def get_qr_cookie_refresh_remaining_time(self) -> int:
|
||||||
|
"""获取扫码登录Cookie刷新剩余冷却时间(秒)"""
|
||||||
|
current_time = time.time()
|
||||||
|
time_since_qr_refresh = current_time - self.last_qr_cookie_refresh_time
|
||||||
|
remaining_time = max(0, self.qr_cookie_refresh_cooldown - time_since_qr_refresh)
|
||||||
|
return int(remaining_time)
|
||||||
|
|
||||||
async def _refresh_cookies_via_browser(self):
|
async def _refresh_cookies_via_browser(self):
|
||||||
"""通过浏览器访问指定页面刷新Cookie"""
|
"""通过浏览器访问指定页面刷新Cookie"""
|
||||||
|
|
||||||
@ -3588,6 +3934,19 @@ class XianyuLive:
|
|||||||
import asyncio
|
import asyncio
|
||||||
from playwright.async_api import async_playwright
|
from playwright.async_api import async_playwright
|
||||||
|
|
||||||
|
# 检查是否需要等待扫码登录Cookie刷新的冷却时间
|
||||||
|
current_time = time.time()
|
||||||
|
time_since_qr_refresh = current_time - self.last_qr_cookie_refresh_time
|
||||||
|
|
||||||
|
if time_since_qr_refresh < self.qr_cookie_refresh_cooldown:
|
||||||
|
remaining_time = self.qr_cookie_refresh_cooldown - time_since_qr_refresh
|
||||||
|
remaining_minutes = int(remaining_time // 60)
|
||||||
|
remaining_seconds = int(remaining_time % 60)
|
||||||
|
|
||||||
|
logger.info(f"【{self.cookie_id}】扫码登录Cookie刷新冷却中,还需等待 {remaining_minutes}分{remaining_seconds}秒")
|
||||||
|
logger.info(f"【{self.cookie_id}】跳过本次浏览器Cookie刷新")
|
||||||
|
return False
|
||||||
|
|
||||||
logger.info(f"【{self.cookie_id}】开始通过浏览器刷新Cookie...")
|
logger.info(f"【{self.cookie_id}】开始通过浏览器刷新Cookie...")
|
||||||
logger.info(f"【{self.cookie_id}】刷新前Cookie长度: {len(self.cookies_str)}")
|
logger.info(f"【{self.cookie_id}】刷新前Cookie长度: {len(self.cookies_str)}")
|
||||||
logger.info(f"【{self.cookie_id}】刷新前Cookie字段数: {len(self.cookies)}")
|
logger.info(f"【{self.cookie_id}】刷新前Cookie字段数: {len(self.cookies)}")
|
||||||
@ -3855,6 +4214,7 @@ class XianyuLive:
|
|||||||
except Exception as cleanup_e:
|
except Exception as cleanup_e:
|
||||||
logger.warning(f"【{self.cookie_id}】清理浏览器资源时出错: {self._safe_str(cleanup_e)}")
|
logger.warning(f"【{self.cookie_id}】清理浏览器资源时出错: {self._safe_str(cleanup_e)}")
|
||||||
|
|
||||||
|
|
||||||
async def send_msg_once(self, toid, item_id, text):
|
async def send_msg_once(self, toid, item_id, text):
|
||||||
headers = {
|
headers = {
|
||||||
"Cookie": self.cookies_str,
|
"Cookie": self.cookies_str,
|
||||||
|
355
reply_server.py
355
reply_server.py
@ -14,6 +14,8 @@ import os
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import io
|
import io
|
||||||
|
import asyncio
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
import cookie_manager
|
import cookie_manager
|
||||||
from db_manager import db_manager
|
from db_manager import db_manager
|
||||||
@ -36,9 +38,30 @@ TOKEN_EXPIRE_TIME = 24 * 60 * 60 # token过期时间:24小时
|
|||||||
# HTTP Bearer认证
|
# HTTP Bearer认证
|
||||||
security = HTTPBearer(auto_error=False)
|
security = HTTPBearer(auto_error=False)
|
||||||
|
|
||||||
|
# 扫码登录检查锁 - 防止并发处理同一个session
|
||||||
|
qr_check_locks = defaultdict(lambda: asyncio.Lock())
|
||||||
|
qr_check_processed = {} # 记录已处理的session: {session_id: {'processed': bool, 'timestamp': float}}
|
||||||
|
|
||||||
# 不再需要单独的密码初始化,由数据库初始化时处理
|
# 不再需要单独的密码初始化,由数据库初始化时处理
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup_qr_check_records():
|
||||||
|
"""清理过期的扫码检查记录"""
|
||||||
|
current_time = time.time()
|
||||||
|
expired_sessions = []
|
||||||
|
|
||||||
|
for session_id, record in qr_check_processed.items():
|
||||||
|
# 清理超过1小时的记录
|
||||||
|
if current_time - record['timestamp'] > 3600:
|
||||||
|
expired_sessions.append(session_id)
|
||||||
|
|
||||||
|
for session_id in expired_sessions:
|
||||||
|
if session_id in qr_check_processed:
|
||||||
|
del qr_check_processed[session_id]
|
||||||
|
if session_id in qr_check_locks:
|
||||||
|
del qr_check_locks[session_id]
|
||||||
|
|
||||||
|
|
||||||
def load_keywords() -> List[Tuple[str, str]]:
|
def load_keywords() -> List[Tuple[str, str]]:
|
||||||
"""读取关键字→回复映射表
|
"""读取关键字→回复映射表
|
||||||
|
|
||||||
@ -1038,26 +1061,57 @@ async def generate_qr_code(current_user: Dict[str, Any] = Depends(get_current_us
|
|||||||
async def check_qr_code_status(session_id: str, current_user: Dict[str, Any] = Depends(get_current_user)):
|
async def check_qr_code_status(session_id: str, current_user: Dict[str, Any] = Depends(get_current_user)):
|
||||||
"""检查扫码登录状态"""
|
"""检查扫码登录状态"""
|
||||||
try:
|
try:
|
||||||
# 清理过期会话
|
# 清理过期记录
|
||||||
qr_login_manager.cleanup_expired_sessions()
|
cleanup_qr_check_records()
|
||||||
|
|
||||||
# 获取会话状态
|
# 检查是否已经处理过
|
||||||
status_info = qr_login_manager.get_session_status(session_id)
|
if session_id in qr_check_processed:
|
||||||
|
record = qr_check_processed[session_id]
|
||||||
|
if record['processed']:
|
||||||
|
log_with_user('debug', f"扫码登录session {session_id} 已处理过,直接返回", current_user)
|
||||||
|
# 返回简单的成功状态,避免重复处理
|
||||||
|
return {'status': 'already_processed', 'message': '该会话已处理完成'}
|
||||||
|
|
||||||
if status_info['status'] == 'success':
|
# 获取该session的锁
|
||||||
# 登录成功,处理Cookie
|
session_lock = qr_check_locks[session_id]
|
||||||
cookies_info = qr_login_manager.get_session_cookies(session_id)
|
|
||||||
if cookies_info:
|
|
||||||
account_info = await process_qr_login_cookies(
|
|
||||||
cookies_info['cookies'],
|
|
||||||
cookies_info['unb'],
|
|
||||||
current_user
|
|
||||||
)
|
|
||||||
status_info['account_info'] = account_info
|
|
||||||
|
|
||||||
log_with_user('info', f"扫码登录成功处理完成: {session_id}, 账号: {account_info.get('account_id', 'unknown')}", current_user)
|
# 使用非阻塞方式尝试获取锁
|
||||||
|
if session_lock.locked():
|
||||||
|
log_with_user('debug', f"扫码登录session {session_id} 正在被其他请求处理,跳过", current_user)
|
||||||
|
return {'status': 'processing', 'message': '正在处理中,请稍候...'}
|
||||||
|
|
||||||
return status_info
|
async with session_lock:
|
||||||
|
# 再次检查是否已处理(双重检查)
|
||||||
|
if session_id in qr_check_processed and qr_check_processed[session_id]['processed']:
|
||||||
|
log_with_user('debug', f"扫码登录session {session_id} 在获取锁后发现已处理,直接返回", current_user)
|
||||||
|
return {'status': 'already_processed', 'message': '该会话已处理完成'}
|
||||||
|
|
||||||
|
# 清理过期会话
|
||||||
|
qr_login_manager.cleanup_expired_sessions()
|
||||||
|
|
||||||
|
# 获取会话状态
|
||||||
|
status_info = qr_login_manager.get_session_status(session_id)
|
||||||
|
|
||||||
|
if status_info['status'] == 'success':
|
||||||
|
# 登录成功,处理Cookie(现在包含获取真实cookie的逻辑)
|
||||||
|
cookies_info = qr_login_manager.get_session_cookies(session_id)
|
||||||
|
if cookies_info:
|
||||||
|
account_info = await process_qr_login_cookies(
|
||||||
|
cookies_info['cookies'],
|
||||||
|
cookies_info['unb'],
|
||||||
|
current_user
|
||||||
|
)
|
||||||
|
status_info['account_info'] = account_info
|
||||||
|
|
||||||
|
log_with_user('info', f"扫码登录处理完成: {session_id}, 账号: {account_info.get('account_id', 'unknown')}", current_user)
|
||||||
|
|
||||||
|
# 标记该session已处理
|
||||||
|
qr_check_processed[session_id] = {
|
||||||
|
'processed': True,
|
||||||
|
'timestamp': time.time()
|
||||||
|
}
|
||||||
|
|
||||||
|
return status_info
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_with_user('error', f"检查扫码登录状态异常: {str(e)}", current_user)
|
log_with_user('error', f"检查扫码登录状态异常: {str(e)}", current_user)
|
||||||
@ -1065,7 +1119,7 @@ async def check_qr_code_status(session_id: str, current_user: Dict[str, Any] = D
|
|||||||
|
|
||||||
|
|
||||||
async def process_qr_login_cookies(cookies: str, unb: str, current_user: Dict[str, Any]) -> Dict[str, Any]:
|
async def process_qr_login_cookies(cookies: str, unb: str, current_user: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""处理扫码登录获取的Cookie"""
|
"""处理扫码登录获取的Cookie - 先获取真实cookie再保存到数据库"""
|
||||||
try:
|
try:
|
||||||
user_id = current_user['user_id']
|
user_id = current_user['user_id']
|
||||||
|
|
||||||
@ -1083,20 +1137,11 @@ async def process_qr_login_cookies(cookies: str, unb: str, current_user: Dict[st
|
|||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 确定账号ID
|
||||||
if existing_account_id:
|
if existing_account_id:
|
||||||
# 更新现有账号的Cookie
|
account_id = existing_account_id
|
||||||
db_manager.save_cookie(existing_account_id, cookies, user_id)
|
is_new_account = False
|
||||||
|
log_with_user('info', f"扫码登录找到现有账号: {account_id}, UNB: {unb}", current_user)
|
||||||
# 更新cookie_manager中的Cookie
|
|
||||||
if cookie_manager.manager:
|
|
||||||
cookie_manager.manager.update_cookie(existing_account_id, cookies)
|
|
||||||
|
|
||||||
log_with_user('info', f"扫码登录更新现有账号Cookie: {existing_account_id}, UNB: {unb}", current_user)
|
|
||||||
|
|
||||||
return {
|
|
||||||
'account_id': existing_account_id,
|
|
||||||
'is_new_account': False
|
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
# 创建新账号,使用unb作为账号ID
|
# 创建新账号,使用unb作为账号ID
|
||||||
account_id = unb
|
account_id = unb
|
||||||
@ -1108,25 +1153,255 @@ async def process_qr_login_cookies(cookies: str, unb: str, current_user: Dict[st
|
|||||||
account_id = f"{original_account_id}_{counter}"
|
account_id = f"{original_account_id}_{counter}"
|
||||||
counter += 1
|
counter += 1
|
||||||
|
|
||||||
# 保存新账号
|
is_new_account = True
|
||||||
db_manager.save_cookie(account_id, cookies, user_id)
|
log_with_user('info', f"扫码登录准备创建新账号: {account_id}, UNB: {unb}", current_user)
|
||||||
|
|
||||||
# 添加到cookie_manager
|
# 第一步:使用扫码cookie获取真实cookie
|
||||||
if cookie_manager.manager:
|
log_with_user('info', f"开始使用扫码cookie获取真实cookie: {account_id}", current_user)
|
||||||
cookie_manager.manager.add_cookie(account_id, cookies)
|
|
||||||
|
|
||||||
log_with_user('info', f"扫码登录创建新账号: {account_id}, UNB: {unb}", current_user)
|
try:
|
||||||
|
# 创建一个临时的XianyuLive实例来执行cookie刷新
|
||||||
|
from XianyuAutoAsync import XianyuLive
|
||||||
|
|
||||||
return {
|
# 使用扫码登录的cookie创建临时实例
|
||||||
'account_id': account_id,
|
temp_instance = XianyuLive(
|
||||||
'is_new_account': True
|
cookies_str=cookies,
|
||||||
}
|
cookie_id=account_id,
|
||||||
|
user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
# 执行cookie刷新获取真实cookie
|
||||||
|
refresh_success = await temp_instance.refresh_cookies_from_qr_login(
|
||||||
|
qr_cookies_str=cookies,
|
||||||
|
cookie_id=account_id,
|
||||||
|
user_id=user_id
|
||||||
|
)
|
||||||
|
|
||||||
|
if refresh_success:
|
||||||
|
log_with_user('info', f"扫码登录真实cookie获取成功: {account_id}", current_user)
|
||||||
|
|
||||||
|
# 从数据库获取刚刚保存的真实cookie
|
||||||
|
updated_cookie_info = db_manager.get_cookie_by_id(account_id)
|
||||||
|
if updated_cookie_info:
|
||||||
|
real_cookies = updated_cookie_info['cookies_str']
|
||||||
|
log_with_user('info', f"已获取真实cookie,长度: {len(real_cookies)}", current_user)
|
||||||
|
|
||||||
|
# 第二步:将真实cookie添加到cookie_manager(如果是新账号)或更新现有账号
|
||||||
|
if cookie_manager.manager:
|
||||||
|
if is_new_account:
|
||||||
|
cookie_manager.manager.add_cookie(account_id, real_cookies)
|
||||||
|
log_with_user('info', f"已将真实cookie添加到cookie_manager: {account_id}", current_user)
|
||||||
|
else:
|
||||||
|
cookie_manager.manager.update_cookie(account_id, real_cookies)
|
||||||
|
log_with_user('info', f"已更新cookie_manager中的真实cookie: {account_id}", current_user)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'account_id': account_id,
|
||||||
|
'is_new_account': is_new_account,
|
||||||
|
'real_cookie_refreshed': True,
|
||||||
|
'cookie_length': len(real_cookies)
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
log_with_user('error', f"无法从数据库获取真实cookie: {account_id}", current_user)
|
||||||
|
# 降级处理:使用原始扫码cookie
|
||||||
|
return await _fallback_save_qr_cookie(account_id, cookies, user_id, is_new_account, current_user, "无法从数据库获取真实cookie")
|
||||||
|
else:
|
||||||
|
log_with_user('warning', f"扫码登录真实cookie获取失败: {account_id}", current_user)
|
||||||
|
# 降级处理:使用原始扫码cookie
|
||||||
|
return await _fallback_save_qr_cookie(account_id, cookies, user_id, is_new_account, current_user, "真实cookie获取失败")
|
||||||
|
|
||||||
|
except Exception as refresh_e:
|
||||||
|
log_with_user('error', f"扫码登录真实cookie获取异常: {str(refresh_e)}", current_user)
|
||||||
|
# 降级处理:使用原始扫码cookie
|
||||||
|
return await _fallback_save_qr_cookie(account_id, cookies, user_id, is_new_account, current_user, f"获取真实cookie异常: {str(refresh_e)}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_with_user('error', f"处理扫码登录Cookie失败: {str(e)}", current_user)
|
log_with_user('error', f"处理扫码登录Cookie失败: {str(e)}", current_user)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
async def _fallback_save_qr_cookie(account_id: str, cookies: str, user_id: int, is_new_account: bool, current_user: Dict[str, Any], error_reason: str) -> Dict[str, Any]:
|
||||||
|
"""降级处理:当无法获取真实cookie时,保存原始扫码cookie"""
|
||||||
|
try:
|
||||||
|
log_with_user('warning', f"降级处理 - 保存原始扫码cookie: {account_id}, 原因: {error_reason}", current_user)
|
||||||
|
|
||||||
|
# 保存原始扫码cookie到数据库
|
||||||
|
if is_new_account:
|
||||||
|
db_manager.save_cookie(account_id, cookies, user_id)
|
||||||
|
log_with_user('info', f"降级处理 - 新账号原始cookie已保存: {account_id}", current_user)
|
||||||
|
else:
|
||||||
|
db_manager.save_cookie(account_id, cookies, user_id)
|
||||||
|
log_with_user('info', f"降级处理 - 现有账号原始cookie已更新: {account_id}", current_user)
|
||||||
|
|
||||||
|
# 添加到或更新cookie_manager
|
||||||
|
if cookie_manager.manager:
|
||||||
|
if is_new_account:
|
||||||
|
cookie_manager.manager.add_cookie(account_id, cookies)
|
||||||
|
log_with_user('info', f"降级处理 - 已将原始cookie添加到cookie_manager: {account_id}", current_user)
|
||||||
|
else:
|
||||||
|
cookie_manager.manager.update_cookie(account_id, cookies)
|
||||||
|
log_with_user('info', f"降级处理 - 已更新cookie_manager中的原始cookie: {account_id}", current_user)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'account_id': account_id,
|
||||||
|
'is_new_account': is_new_account,
|
||||||
|
'real_cookie_refreshed': False,
|
||||||
|
'fallback_reason': error_reason,
|
||||||
|
'cookie_length': len(cookies)
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as fallback_e:
|
||||||
|
log_with_user('error', f"降级处理失败: {str(fallback_e)}", current_user)
|
||||||
|
raise fallback_e
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/qr-login/refresh-cookies")
|
||||||
|
async def refresh_cookies_from_qr_login(
|
||||||
|
request: Dict[str, Any],
|
||||||
|
current_user: Dict[str, Any] = Depends(get_current_user)
|
||||||
|
):
|
||||||
|
"""使用扫码登录获取的cookie访问指定界面获取真实cookie并存入数据库"""
|
||||||
|
try:
|
||||||
|
qr_cookies = request.get('qr_cookies')
|
||||||
|
cookie_id = request.get('cookie_id')
|
||||||
|
|
||||||
|
if not qr_cookies:
|
||||||
|
return {'success': False, 'message': '缺少扫码登录cookie'}
|
||||||
|
|
||||||
|
if not cookie_id:
|
||||||
|
return {'success': False, 'message': '缺少cookie_id'}
|
||||||
|
|
||||||
|
log_with_user('info', f"开始使用扫码cookie刷新真实cookie: {cookie_id}", current_user)
|
||||||
|
|
||||||
|
# 创建一个临时的XianyuLive实例来执行cookie刷新
|
||||||
|
from XianyuAutoAsync import XianyuLive
|
||||||
|
|
||||||
|
# 使用扫码登录的cookie创建临时实例
|
||||||
|
temp_instance = XianyuLive(
|
||||||
|
cookies_str=qr_cookies,
|
||||||
|
cookie_id=cookie_id,
|
||||||
|
user_id=current_user['user_id']
|
||||||
|
)
|
||||||
|
|
||||||
|
# 执行cookie刷新
|
||||||
|
success = await temp_instance.refresh_cookies_from_qr_login(
|
||||||
|
qr_cookies_str=qr_cookies,
|
||||||
|
cookie_id=cookie_id,
|
||||||
|
user_id=current_user['user_id']
|
||||||
|
)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
log_with_user('info', f"扫码cookie刷新成功: {cookie_id}", current_user)
|
||||||
|
|
||||||
|
# 如果cookie_manager存在,更新其中的cookie
|
||||||
|
if cookie_manager.manager:
|
||||||
|
# 从数据库获取更新后的cookie
|
||||||
|
updated_cookie_info = db_manager.get_cookie_by_id(cookie_id)
|
||||||
|
if updated_cookie_info:
|
||||||
|
cookie_manager.manager.update_cookie(cookie_id, updated_cookie_info['cookies_str'])
|
||||||
|
log_with_user('info', f"已更新cookie_manager中的cookie: {cookie_id}", current_user)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'message': '真实cookie获取并保存成功',
|
||||||
|
'cookie_id': cookie_id
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
log_with_user('error', f"扫码cookie刷新失败: {cookie_id}", current_user)
|
||||||
|
return {'success': False, 'message': '获取真实cookie失败'}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log_with_user('error', f"扫码cookie刷新异常: {str(e)}", current_user)
|
||||||
|
return {'success': False, 'message': f'刷新cookie失败: {str(e)}'}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/qr-login/reset-cooldown/{cookie_id}")
|
||||||
|
async def reset_qr_cookie_refresh_cooldown(
|
||||||
|
cookie_id: str,
|
||||||
|
current_user: Dict[str, Any] = Depends(get_current_user)
|
||||||
|
):
|
||||||
|
"""重置指定账号的扫码登录Cookie刷新冷却时间"""
|
||||||
|
try:
|
||||||
|
log_with_user('info', f"重置扫码登录Cookie刷新冷却时间: {cookie_id}", current_user)
|
||||||
|
|
||||||
|
# 检查cookie是否存在
|
||||||
|
cookie_info = db_manager.get_cookie_by_id(cookie_id)
|
||||||
|
if not cookie_info:
|
||||||
|
return {'success': False, 'message': '账号不存在'}
|
||||||
|
|
||||||
|
# 如果cookie_manager中有对应的实例,直接重置
|
||||||
|
if cookie_manager.manager and cookie_id in cookie_manager.manager.instances:
|
||||||
|
instance = cookie_manager.manager.instances[cookie_id]
|
||||||
|
remaining_time_before = instance.get_qr_cookie_refresh_remaining_time()
|
||||||
|
instance.reset_qr_cookie_refresh_flag()
|
||||||
|
|
||||||
|
log_with_user('info', f"已重置账号 {cookie_id} 的扫码登录冷却时间,原剩余时间: {remaining_time_before}秒", current_user)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'message': '扫码登录Cookie刷新冷却时间已重置',
|
||||||
|
'cookie_id': cookie_id,
|
||||||
|
'previous_remaining_time': remaining_time_before
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
# 如果没有活跃实例,返回成功(因为没有冷却时间需要重置)
|
||||||
|
log_with_user('info', f"账号 {cookie_id} 没有活跃实例,无需重置冷却时间", current_user)
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'message': '账号没有活跃实例,无需重置冷却时间',
|
||||||
|
'cookie_id': cookie_id
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log_with_user('error', f"重置扫码登录冷却时间异常: {str(e)}", current_user)
|
||||||
|
return {'success': False, 'message': f'重置冷却时间失败: {str(e)}'}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/qr-login/cooldown-status/{cookie_id}")
|
||||||
|
async def get_qr_cookie_refresh_cooldown_status(
|
||||||
|
cookie_id: str,
|
||||||
|
current_user: Dict[str, Any] = Depends(get_current_user)
|
||||||
|
):
|
||||||
|
"""获取指定账号的扫码登录Cookie刷新冷却状态"""
|
||||||
|
try:
|
||||||
|
# 检查cookie是否存在
|
||||||
|
cookie_info = db_manager.get_cookie_by_id(cookie_id)
|
||||||
|
if not cookie_info:
|
||||||
|
return {'success': False, 'message': '账号不存在'}
|
||||||
|
|
||||||
|
# 如果cookie_manager中有对应的实例,获取冷却状态
|
||||||
|
if cookie_manager.manager and cookie_id in cookie_manager.manager.instances:
|
||||||
|
instance = cookie_manager.manager.instances[cookie_id]
|
||||||
|
remaining_time = instance.get_qr_cookie_refresh_remaining_time()
|
||||||
|
cooldown_duration = instance.qr_cookie_refresh_cooldown
|
||||||
|
last_refresh_time = instance.last_qr_cookie_refresh_time
|
||||||
|
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'cookie_id': cookie_id,
|
||||||
|
'remaining_time': remaining_time,
|
||||||
|
'cooldown_duration': cooldown_duration,
|
||||||
|
'last_refresh_time': last_refresh_time,
|
||||||
|
'is_in_cooldown': remaining_time > 0,
|
||||||
|
'remaining_minutes': remaining_time // 60,
|
||||||
|
'remaining_seconds': remaining_time % 60
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'cookie_id': cookie_id,
|
||||||
|
'remaining_time': 0,
|
||||||
|
'cooldown_duration': 600, # 默认10分钟
|
||||||
|
'last_refresh_time': 0,
|
||||||
|
'is_in_cooldown': False,
|
||||||
|
'message': '账号没有活跃实例'
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log_with_user('error', f"获取扫码登录冷却状态异常: {str(e)}", current_user)
|
||||||
|
return {'success': False, 'message': f'获取冷却状态失败: {str(e)}'}
|
||||||
|
|
||||||
|
|
||||||
@app.put('/cookies/{cid}/status')
|
@app.put('/cookies/{cid}/status')
|
||||||
def update_cookie_status(cid: str, status_data: CookieStatusIn, current_user: Dict[str, Any] = Depends(get_current_user)):
|
def update_cookie_status(cid: str, status_data: CookieStatusIn, current_user: Dict[str, Any] = Depends(get_current_user)):
|
||||||
"""更新账号的启用/禁用状态"""
|
"""更新账号的启用/禁用状态"""
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
/* ================================
|
/* ================================
|
||||||
通用卡片样式 - 适用于所有菜单的卡片
|
通用卡片样式 - 适用于所有菜单的卡片
|
||||||
================================ */
|
================================ */
|
||||||
|
|
||||||
|
/* 旋转动画 */
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.spin {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
.card {
|
.card {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
|
@ -244,17 +244,17 @@
|
|||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="d-grid gap-2 d-md-flex justify-content-md-center">
|
<div class="d-grid gap-2 d-md-flex justify-content-md-center">
|
||||||
<button type="button" class="btn btn-success btn-lg me-md-2 flex-fill qr-login-btn" onclick="showQRCodeLogin()" style="max-width: 300px;">
|
<button type="button" class="btn btn-lg me-md-2 flex-fill qr-login-btn" onclick="showQRCodeLogin()" style="max-width: 300px;">
|
||||||
<i class="bi bi-qr-code me-2"></i>
|
<i class="bi bi-qr-code me-2"></i>
|
||||||
<span class="fw-bold">扫码登录</span>
|
<span class="fw-bold">扫码登录</span>
|
||||||
<br>
|
<br>
|
||||||
<small class="opacity-75">推荐方式,安全便捷</small>
|
<small class="opacity-75">不推荐,一般都不成功</small>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-outline-secondary btn-lg flex-fill manual-input-btn" onclick="toggleManualInput()" style="max-width: 300px;">
|
<button type="button" class="btn btn-outline-secondary btn-lg flex-fill manual-input-btn" onclick="toggleManualInput()" style="max-width: 300px;">
|
||||||
<i class="bi bi-keyboard me-2"></i>
|
<i class="bi bi-keyboard me-2"></i>
|
||||||
<span class="fw-bold">手动输入</span>
|
<span class="fw-bold">手动输入</span>
|
||||||
<br>
|
<br>
|
||||||
<small class="opacity-75">输入Cookie信息</small>
|
<small class="opacity-75">推荐方式,使用消息界面的cookie</small>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
194
static/js/app.js
194
static/js/app.js
@ -1347,6 +1347,157 @@ function copyCookie(id, value) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 刷新真实Cookie
|
||||||
|
async function refreshRealCookie(cookieId) {
|
||||||
|
if (!cookieId) {
|
||||||
|
showToast('缺少账号ID', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前cookie值
|
||||||
|
try {
|
||||||
|
const cookieDetails = await fetchJSON(`${apiBase}/cookies/details`);
|
||||||
|
const currentCookie = cookieDetails.find(c => c.id === cookieId);
|
||||||
|
|
||||||
|
if (!currentCookie || !currentCookie.value) {
|
||||||
|
showToast('未找到有效的Cookie信息', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认操作
|
||||||
|
if (!confirm(`确定要刷新账号 "${cookieId}" 的真实Cookie吗?\n\n此操作将使用当前Cookie访问闲鱼IM界面获取最新的真实Cookie。`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示加载状态
|
||||||
|
const button = event.target.closest('button');
|
||||||
|
const originalContent = button.innerHTML;
|
||||||
|
button.disabled = true;
|
||||||
|
button.innerHTML = '<i class="bi bi-arrow-clockwise spin"></i>';
|
||||||
|
|
||||||
|
// 调用刷新API
|
||||||
|
const response = await fetch(`${apiBase}/qr-login/refresh-cookies`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${authToken}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
qr_cookies: currentCookie.value,
|
||||||
|
cookie_id: cookieId
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
showToast(`账号 "${cookieId}" 真实Cookie刷新成功`, 'success');
|
||||||
|
// 刷新账号列表以显示更新后的cookie
|
||||||
|
loadCookies();
|
||||||
|
} else {
|
||||||
|
showToast(`真实Cookie刷新失败: ${result.message}`, 'danger');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('刷新真实Cookie失败:', error);
|
||||||
|
showToast(`刷新真实Cookie失败: ${error.message || '未知错误'}`, 'danger');
|
||||||
|
} finally {
|
||||||
|
// 恢复按钮状态
|
||||||
|
const button = event.target.closest('button');
|
||||||
|
if (button) {
|
||||||
|
button.disabled = false;
|
||||||
|
button.innerHTML = '<i class="bi bi-arrow-clockwise"></i>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示冷却状态
|
||||||
|
async function showCooldownStatus(cookieId) {
|
||||||
|
if (!cookieId) {
|
||||||
|
showToast('缺少账号ID', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${apiBase}/qr-login/cooldown-status/${cookieId}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${authToken}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
const { remaining_time, cooldown_duration, is_in_cooldown, remaining_minutes, remaining_seconds } = result;
|
||||||
|
|
||||||
|
let statusMessage = `账号: ${cookieId}\n`;
|
||||||
|
statusMessage += `冷却时长: ${cooldown_duration / 60}分钟\n`;
|
||||||
|
|
||||||
|
if (is_in_cooldown) {
|
||||||
|
statusMessage += `冷却状态: 进行中\n`;
|
||||||
|
statusMessage += `剩余时间: ${remaining_minutes}分${remaining_seconds}秒\n\n`;
|
||||||
|
statusMessage += `在冷却期间,_refresh_cookies_via_browser 方法将被跳过。\n\n`;
|
||||||
|
statusMessage += `是否要重置冷却时间?`;
|
||||||
|
|
||||||
|
if (confirm(statusMessage)) {
|
||||||
|
await resetCooldownTime(cookieId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
statusMessage += `冷却状态: 无冷却\n`;
|
||||||
|
statusMessage += `可以正常执行 _refresh_cookies_via_browser 方法`;
|
||||||
|
alert(statusMessage);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showToast(`获取冷却状态失败: ${result.message}`, 'danger');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取冷却状态失败:', error);
|
||||||
|
showToast(`获取冷却状态失败: ${error.message || '未知错误'}`, 'danger');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置冷却时间
|
||||||
|
async function resetCooldownTime(cookieId) {
|
||||||
|
if (!cookieId) {
|
||||||
|
showToast('缺少账号ID', 'warning');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${apiBase}/qr-login/reset-cooldown/${cookieId}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${authToken}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
const previousTime = result.previous_remaining_time || 0;
|
||||||
|
const previousMinutes = Math.floor(previousTime / 60);
|
||||||
|
const previousSeconds = previousTime % 60;
|
||||||
|
|
||||||
|
let message = `账号 "${cookieId}" 的扫码登录冷却时间已重置`;
|
||||||
|
if (previousTime > 0) {
|
||||||
|
message += `\n原剩余时间: ${previousMinutes}分${previousSeconds}秒`;
|
||||||
|
}
|
||||||
|
|
||||||
|
showToast(message, 'success');
|
||||||
|
} else {
|
||||||
|
showToast(`重置冷却时间失败: ${result.message}`, 'danger');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('重置冷却时间失败:', error);
|
||||||
|
showToast(`重置冷却时间失败: ${error.message || '未知错误'}`, 'danger');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 删除Cookie
|
// 删除Cookie
|
||||||
async function delCookie(id) {
|
async function delCookie(id) {
|
||||||
if (!confirm(`确定要删除账号 "${id}" 吗?此操作不可恢复。`)) return;
|
if (!confirm(`确定要删除账号 "${id}" 吗?此操作不可恢复。`)) return;
|
||||||
@ -7015,6 +7166,16 @@ async function checkQRCodeStatus() {
|
|||||||
clearQRCodeCheck();
|
clearQRCodeCheck();
|
||||||
showVerificationRequired(data);
|
showVerificationRequired(data);
|
||||||
break;
|
break;
|
||||||
|
case 'processing':
|
||||||
|
document.getElementById('statusText').textContent = '正在处理中...';
|
||||||
|
// 继续轮询,不清理检查
|
||||||
|
break;
|
||||||
|
case 'already_processed':
|
||||||
|
document.getElementById('statusText').textContent = '登录已完成';
|
||||||
|
document.getElementById('statusSpinner').style.display = 'none';
|
||||||
|
clearQRCodeCheck();
|
||||||
|
showToast('该扫码会话已处理完成', 'info');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -7078,12 +7239,37 @@ function showVerificationRequired(data) {
|
|||||||
// 处理扫码成功
|
// 处理扫码成功
|
||||||
function handleQRCodeSuccess(data) {
|
function handleQRCodeSuccess(data) {
|
||||||
if (data.account_info) {
|
if (data.account_info) {
|
||||||
const { account_id, is_new_account } = data.account_info;
|
const { account_id, is_new_account, real_cookie_refreshed, fallback_reason, cookie_length } = data.account_info;
|
||||||
|
|
||||||
|
// 构建成功消息
|
||||||
|
let successMessage = '';
|
||||||
if (is_new_account) {
|
if (is_new_account) {
|
||||||
showToast(`新账号添加成功!账号ID: ${account_id}`, 'success');
|
successMessage = `新账号添加成功!账号ID: ${account_id}`;
|
||||||
} else {
|
} else {
|
||||||
showToast(`账号Cookie已更新!账号ID: ${account_id}`, 'success');
|
successMessage = `账号Cookie已更新!账号ID: ${account_id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加cookie长度信息
|
||||||
|
if (cookie_length) {
|
||||||
|
successMessage += `\nCookie长度: ${cookie_length}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加真实cookie获取状态信息
|
||||||
|
if (real_cookie_refreshed === true) {
|
||||||
|
successMessage += '\n✅ 真实Cookie获取并保存成功';
|
||||||
|
document.getElementById('statusText').textContent = '登录成功!真实Cookie已获取并保存';
|
||||||
|
showToast(successMessage, 'success');
|
||||||
|
} else if (real_cookie_refreshed === false) {
|
||||||
|
successMessage += '\n⚠️ 真实Cookie获取失败,已保存原始扫码Cookie';
|
||||||
|
if (fallback_reason) {
|
||||||
|
successMessage += `\n原因: ${fallback_reason}`;
|
||||||
|
}
|
||||||
|
document.getElementById('statusText').textContent = '登录成功,但使用原始Cookie';
|
||||||
|
showToast(successMessage, 'warning');
|
||||||
|
} else {
|
||||||
|
// 兼容旧版本,没有真实cookie刷新信息
|
||||||
|
document.getElementById('statusText').textContent = '登录成功!';
|
||||||
|
showToast(successMessage, 'success');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭模态框
|
// 关闭模态框
|
||||||
@ -7093,7 +7279,7 @@ function handleQRCodeSuccess(data) {
|
|||||||
|
|
||||||
// 刷新账号列表
|
// 刷新账号列表
|
||||||
loadCookies();
|
loadCookies();
|
||||||
}, 2000);
|
}, 3000); // 延长显示时间以便用户看到详细信息
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user