From ed95ce5b232ea48a2838c1c86a6a7fe78a55f2ea Mon Sep 17 00:00:00 2001 From: bwstudio <7469504@qq.com> Date: Sun, 8 Mar 2026 09:20:03 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E7=9B=91=E6=8E=A7=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B8=85=E7=90=86=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- browser_manager.py | 14 +++- config.json | 20 ++--- cookie_data.json | 160 +++++++++++++++++++++++++++++++++++++- monitor.py | 188 +++++++++++++++++++++++++++++++++++---------- monitor.spec | 44 +++++++++++ notify_state.json | 14 ++++ 6 files changed, 383 insertions(+), 57 deletions(-) create mode 100644 monitor.spec create mode 100644 notify_state.json diff --git a/browser_manager.py b/browser_manager.py index 82181a2..6ca2103 100644 --- a/browser_manager.py +++ b/browser_manager.py @@ -70,7 +70,12 @@ class BrowserManager: if not self.ensure_connection(): return 0 + if not cookies: + return 0 + success_count = 0 + # 直接使用DrissionPage的set.cookies方法批量导入 + # 该方法内部已经优化了导入逻辑 for cookie in cookies: try: if not cookie.get('name') or not cookie.get('value'): @@ -86,9 +91,9 @@ class BrowserManager: self.page.set.cookies(cookie_dict) success_count += 1 except Exception as e: - logger.error(f"导入cookie失败: {cookie.get('name')} - {e}") + # 忽略导入失败的cookie,继续处理其他cookie + pass - logger.info(f"成功导入 {success_count} 个 cookies") return success_count def import_local_storage(self, local_storage: dict): @@ -137,11 +142,13 @@ class BrowserManager: try: self.page.get(url) self.wait_page_loaded() + time.sleep(3) # 等待网页加载完毕并暂停3秒 for i in range(3): - time.sleep(1) + time.sleep(3) # 每次刷新前暂停3秒 self.page.refresh() self.wait_page_loaded() + time.sleep(3) # 每次刷新后暂停3秒 if not check_selector and not success_text: return False @@ -223,4 +230,3 @@ class BrowserManager: logger.error(f"关闭浏览器失败: {e}") finally: self.page = None - logger.info("浏览器已关闭") diff --git a/config.json b/config.json index 79113e5..43f67f1 100644 --- a/config.json +++ b/config.json @@ -8,9 +8,7 @@ "api_url": "https://movie-pilot.org/cookiecloud" }, "notification": { - "iyuu_token": "IYUU37629Tc1d371c7ce99a49ff9778e196286b7e4592be191", - "max_fail_count": 3, - "notify_on_success": true + "iyuu_token": "IYUU37629Tc1d371c7ce99a49ff9778e196286b7e4592be191" }, "browser": { "type": "edge", @@ -33,20 +31,24 @@ "api_url": "http://192.168.1.100:3000/cookiecloud" }, "notification": { - "iyuu_token": "IYUU37629Tc1d371c7ce99a49ff9778e196286b7e4592be191", - "max_fail_count": 3, - "notify_on_success": true + "iyuu_token": "IYUU37629Tc1d371c7ce99a49ff9778e196286b7e4592be191" }, "browser": { "type": "edge", "headless": false }, "websites": [ + { + "name": "柴油联名卡", + "url": "https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll", + "login_check_selector": "", + "success_text": "彭峰" + }, { "name": "网站B", "url": "https://192.168.1.200:8006/", "login_check_selector": "", - "success_text": "彭峰" + "success_text": "root@pam" } ] }, @@ -58,9 +60,7 @@ "api_url": "https://movie-pilot.org/cookiecloud" }, "notification": { - "iyuu_token": "IYUU37629Tc1d371c7ce99a49ff9778e196286b7e4592be191", - "max_fail_count": 3, - "notify_on_success": true + "iyuu_token": "IYUU37629Tc1d371c7ce99a49ff9778e196286b7e4592be191" }, "browser": { "type": "edge", diff --git a/cookie_data.json b/cookie_data.json index 3851720..0fff262 100644 --- a/cookie_data.json +++ b/cookie_data.json @@ -1,21 +1,175 @@ { "小余": { "lmkbi.95155.com": { - "cookies": [], + "cookies": [ + { + "name": "_qimei_i_1", + "value": "5bd854d4c10f0489c79eff300ad57ae0f1bba6f017520a84e6862c582493206c616336c03980ebdd829cd4f1", + "domain": ".95155.com" + }, + { + "name": "userType", + "value": "2", + "domain": "lmkbi.95155.com" + }, + { + "name": "_qimei_fingerprint", + "value": "46d124002cf7d99787d08337dcd33c84", + "domain": ".95155.com" + }, + { + "name": "LMKBIUSS", + "value": "eyJhY2NvdW50SWQiOjIzMzk0OTQ3NTAwMjA0OSwicGhvbmUiOiIxMzU0ODU2OTY2NiIsInRpbWVzdGFtcCI6MTc3Mjg4NzYxMjgwOCwidG9rZW4iOiI3YWM0YjZkMS1lYzc2LTQ3NDgtOTM4MS1kZDUwZGI4NjM1NTEifQ==", + "domain": "lmkbi.95155.com" + }, + { + "name": "edentoken", + "value": "13548569666", + "domain": "lmkbi.95155.com" + }, + { + "name": "_qimei_uuid42", + "value": "1991d0e1933100350aa906e215996b94a17042ee55", + "domain": ".95155.com" + }, + { + "name": "user", + "value": "13548569666", + "domain": "lmkbi.95155.com" + }, + { + "name": "_qimei_h38", + "value": "fdedb6910aa906e215996b9402000001e1991d", + "domain": ".95155.com" + }, + { + "name": "_qimei_i_3", + "value": "5be076d19c08548d939eff62098270e7f3e7a4f3145c0a8ab7dd2b0d24c5246b336337943c89e2aa8cb7", + "domain": ".95155.com" + } + ], "url": "https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll", "website_name": "柴油联名卡" } }, "彭峰(个人)": { "192.168.1.200:8006": { - "cookies": [], + "cookies": [ + { + "name": "PVELangCookie", + "value": "zh_CN", + "domain": "192.168.1.200" + }, + { + "name": "PVEAuthCookie", + "value": "PVE%3Aroot@pam%3A69ACCBCF%3A%3AwZbqJgSmRXadHue3IxPXURT4YqLAH5XluGp6nedMrZDn01QLvg4yjk1P1Nf08FXzZoH75gxA1USwhZXRbgWn9azugDgWhmwTF5eL2y4xWmYBhYP5Gk+J64hb2OgIr4WoJvxXcUceAFYurDorboz7ILSgJ+Rbwua+rPnmUV2qzYBjSw8r1AyOYTjbdjVjoTM4Erls1JMz1zTCOJFLcEQNgsR0BnXa5VmIb5rp8inRUkA8hRkuDtMp5+HnaF2R+KOEf9CmqjO5nf/48mlGYtg3Bj0wktIO/oX66fRJ4xljIoByLYJhgz03XrarIXxh3o+Foh2yFvhbLpRiwO2QjHQqeA%3D%3D", + "domain": "192.168.1.200" + } + ], "url": "https://192.168.1.200:8006/", "website_name": "网站B" + }, + "lmkbi.95155.com": { + "cookies": [ + { + "name": "_qimei_uuid42", + "value": "1a302130f16100ca18af370a47cb8afce807c4a076", + "domain": ".95155.com" + }, + { + "name": "_qimei_fingerprint", + "value": "50aa79f919511142818459f44ac38dee", + "domain": ".95155.com" + }, + { + "name": "edentoken", + "value": "13548569666", + "domain": "lmkbi.95155.com" + }, + { + "name": "LMKBIUSS", + "value": "eyJhY2NvdW50SWQiOjIzMzk0OTQ3NTAwMjA0OSwicGhvbmUiOiIxMzU0ODU2OTY2NiIsInRpbWVzdGFtcCI6MTc3Mjg4NzYxMjgwOCwidG9rZW4iOiI3YWM0YjZkMS1lYzc2LTQ3NDgtOTM4MS1kZDUwZGI4NjM1NTEifQ==", + "domain": "lmkbi.95155.com" + }, + { + "name": "userType", + "value": "2", + "domain": "lmkbi.95155.com" + }, + { + "name": "_qimei_i_1", + "value": "23ed70d49d0f50dcc497f83153d525e3f0eef5f51508518ae5d97b582493206c6163649c39d8e1dcd4b1e482", + "domain": ".95155.com" + }, + { + "name": "_qimei_i_3", + "value": "64c844d0c35e05dc9796fc355d8470e5a3eba5f0410e00d3e78c2d0e2f95293d306031943c89e29eb6a6", + "domain": ".95155.com" + }, + { + "name": "user", + "value": "13548569666", + "domain": "lmkbi.95155.com" + }, + { + "name": "_qimei_h38", + "value": "f8e0750618af370a47cb8afc0200000c91a302", + "domain": ".95155.com" + } + ], + "url": "https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll", + "website_name": "柴油联名卡" } }, "彭峰(公司)": { "lmkbi.95155.com": { - "cookies": [], + "cookies": [ + { + "name": "_qimei_h38", + "value": "fdedb6910aa906e215996b9402000001e1991d", + "domain": ".95155.com" + }, + { + "name": "userType", + "value": "2", + "domain": "lmkbi.95155.com" + }, + { + "name": "LMKBIUSS", + "value": "eyJhY2NvdW50SWQiOjIzMzk0OTQ3NTAwMjA0OSwicGhvbmUiOiIxMzU0ODU2OTY2NiIsInRpbWVzdGFtcCI6MTc3Mjg4NzYxMjgwOCwidG9rZW4iOiI3YWM0YjZkMS1lYzc2LTQ3NDgtOTM4MS1kZDUwZGI4NjM1NTEifQ==", + "domain": "lmkbi.95155.com" + }, + { + "name": "edentoken", + "value": "13548569666", + "domain": "lmkbi.95155.com" + }, + { + "name": "_qimei_i_3", + "value": "5be076d19c08548d939eff62098270e7f3e7a4f3145c0a8ab7dd2b0d24c5246b336337943c89e2aa8cb7", + "domain": ".95155.com" + }, + { + "name": "_qimei_i_1", + "value": "5bd854d4c10f0489c79eff300ad57ae0f1bba6f017520a84e6862c582493206c616336c03980ebdd829cd4f1", + "domain": ".95155.com" + }, + { + "name": "user", + "value": "13548569666", + "domain": "lmkbi.95155.com" + }, + { + "name": "_qimei_fingerprint", + "value": "46d124002cf7d99787d08337dcd33c84", + "domain": ".95155.com" + }, + { + "name": "_qimei_uuid42", + "value": "1991d0e1933100350aa906e215996b94a17042ee55", + "domain": ".95155.com" + } + ], "url": "https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll", "website_name": "柴油联名卡" }, diff --git a/monitor.py b/monitor.py index 5a0bc1b..49e8d1d 100644 --- a/monitor.py +++ b/monitor.py @@ -10,7 +10,7 @@ from logger import get_logger logger = get_logger(__name__) -NOTIFY_TIMES = [6, 22] +NOTIFY_TIMES = [6, 21] STATE_FILE = "notify_state.json" @@ -39,7 +39,8 @@ def save_notify_state(state: dict): def should_send_notification(user_name: str) -> bool: """判断是否应该发送通知""" - return False + # 去掉时间限制,每次都发送通知 + return True def mark_notification_sent(user_name: str): @@ -78,7 +79,7 @@ class CookieMonitor: user_config: 用户配置字典 """ user_name = user_config.get('name', '未知用户') - logger.info(f"{'='*20} 开始处理用户: {user_name} {'='*20}") + logger.info(f"开始处理用户: {user_name}") websites = user_config.get('websites', []) @@ -101,10 +102,8 @@ class CookieMonitor: } try: - logger.info(f"步骤1: 从本地 cookie_data.json 读取用户 cookies") local_cookies = self.cookie_data_manager.get_user_cookies(user_name) - - logger.info(f"步骤1.5: 从 Cookie Cloud 获取所有 cookies") + cookie_cloud_config = user_config.get('cookie_cloud', {}) cookie_cloud = CookieCloud( api_url=cookie_cloud_config.get('api_url', ''), @@ -112,33 +111,150 @@ class CookieMonitor: password=cookie_cloud_config.get('password', '') ) all_cookies = cookie_cloud.get_cookies() - total_domains = len(all_cookies) - total_cookies = sum(len(c) for c in all_cookies.values()) - logger.info(f"获取到 {total_domains} 个域名,共 {total_cookies} 个 cookie") - logger.info(f"步骤2: 初始化浏览器") browser.init_browser() - logger.info(f"步骤3: 依次验证各网站登录状态") - for website in websites: - result = self.process_website( - user_name=user_name, - website_config=website, - browser=browser, - all_cookies=all_cookies, - local_cookies=local_cookies - ) - user_results['websites'].append(result) + login_failed = False + failed_website = None - if result['success']: + # 初始流程:逐个网站验证,使用本地cookie + for website in websites: + website_name = website.get('name', '未知网站') + url = website.get('url', '') + parsed = urlparse(url) + domain = parsed.netloc + + domain_cookies = [] + for cookie_domain, cookie_data in local_cookies.items(): + clean_domain = cookie_domain.lstrip('.') + if domain in clean_domain or clean_domain in domain: + if isinstance(cookie_data, dict): + cookies = cookie_data.get('cookies', []) + elif isinstance(cookie_data, list): + cookies = cookie_data + else: + cookies = [] + domain_cookies.extend(cookies) + + if domain_cookies: + success_count = browser.import_cookies(domain_cookies, domain) + + login_check_selector = website.get('login_check_selector', '') + success_text = website.get('success_text', '') + + login_success = browser.verify_login( + url=url, + check_selector=login_check_selector, + success_text=success_text + ) + + if login_success: + current_cookies = browser.get_cookies() + if current_cookies: + self.cookie_data_manager.save_user_cookies( + user_name=user_name, + cookies=current_cookies, + url=url, + website_name=website_name + ) + + user_results['websites'].append({ + 'name': website_name, + 'url': url, + 'success': True, + 'error': '' + }) user_results['success_count'] += 1 else: - user_results['fail_count'] += 1 - + login_failed = True + failed_website = website + break + + # 错误处理机制:如果任何一个网站登录失败 + if login_failed: + browser.close() + + browser = BrowserManager( + browser_type=browser_config.get('type', 'edge'), + headless=browser_config.get('headless', False) + ) + browser.init_browser() + + relevant_cookies = [] + + target_domains = [] + for website in websites: + url = website.get('url', '') + if url: + parsed = urlparse(url) + target_domains.append(parsed.netloc) + + for domain_cookies in all_cookies.values(): + if isinstance(domain_cookies, dict): + cookies = domain_cookies.get('cookies', []) + elif isinstance(domain_cookies, list): + cookies = domain_cookies + else: + cookies = [] + + for cookie in cookies: + cookie_domain = cookie.get('domain', '') + if cookie_domain: + clean_cookie_domain = cookie_domain.lstrip('.') + for target_domain in target_domains: + clean_target_domain = target_domain.lstrip('.') + if clean_cookie_domain in clean_target_domain or clean_target_domain in clean_cookie_domain: + relevant_cookies.append(cookie) + break + + if relevant_cookies: + browser.import_cookies(relevant_cookies, domain='') + + user_results['websites'] = [] + user_results['success_count'] = 0 + user_results['fail_count'] = 0 + + for website in websites: + website_name = website.get('name', '未知网站') + url = website.get('url', '') + login_check_selector = website.get('login_check_selector', '') + success_text = website.get('success_text', '') + + login_success = browser.verify_login( + url=url, + check_selector=login_check_selector, + success_text=success_text + ) + + if login_success: + current_cookies = browser.get_cookies() + if current_cookies: + self.cookie_data_manager.save_user_cookies( + user_name=user_name, + cookies=current_cookies, + url=url, + website_name=website_name + ) + + user_results['websites'].append({ + 'name': website_name, + 'url': url, + 'success': True, + 'error': '' + }) + user_results['success_count'] += 1 + else: + user_results['websites'].append({ + 'name': website_name, + 'url': url, + 'success': False, + 'error': '登录状态验证失败' + }) + user_results['fail_count'] += 1 + self.all_results.append(user_results) self._print_summary(user_name, user_results['websites']) - logger.info(f"\n检查用户 {user_name} 是否需要发送通知") if should_send_notification(user_name): self._send_user_notification(user_results) @@ -304,44 +420,38 @@ class CookieMonitor: notifier = create_notification_manager(notification_config) if not notifier: - logger.warning(f"用户 {user_name} 未配置通知") return success_count = user_result['success_count'] fail_count = user_result['fail_count'] - title = f"【Cookie监控】{user_name} - 成功{success_count} 失败{fail_count}" + title = f"Cookie登录" content_lines = [ - f"**{user_name} Cookie监控报告**", + f"**{user_name} Cookie登录报告**", f"", - f"监控时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", + f"登录时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", f"成功: {success_count} 个网站", f"失败: {fail_count} 个网站", f"", ] for website in user_result['websites']: - status = "✓ 成功" if website['success'] else f"✗ 失败: {website['error']}" - content_lines.append(f" {website['name']}: {status}") + status = "成功" if website['success'] else f"失败: {website['error']}" + content_lines.append(f"- {website['name']}: {status}") content = "\n".join(content_lines) - logger.info(f"\n发送用户通知: {user_name}") - logger.info(f"Title: {title}") - if notifier.send(title, content): - logger.info(f"✓ 用户 {user_name} 通知发送成功") mark_notification_sent(user_name) else: - logger.error(f"✗ 用户 {user_name} 通知发送失败") + logger.error(f"用户 {user_name} 通知发送失败") + + def run(self): """运行监控""" - logger.info("="*50) logger.info("Cookie 监控开始") - logger.info(f"时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") - logger.info("="*50) users = self.config_manager.get_users() @@ -355,9 +465,7 @@ class CookieMonitor: except Exception as e: logger.error(f"处理用户失败: {e}") - logger.info("\n" + "="*50) logger.info("Cookie 监控结束") - logger.info("="*50) def main(): diff --git a/monitor.spec b/monitor.spec new file mode 100644 index 0000000..dba6602 --- /dev/null +++ b/monitor.spec @@ -0,0 +1,44 @@ +# -*- mode: python ; coding: utf-8 -*- + + +a = Analysis( + ['monitor.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='monitor', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) +coll = COLLECT( + exe, + a.binaries, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='monitor', +) diff --git a/notify_state.json b/notify_state.json new file mode 100644 index 0000000..3b704e8 --- /dev/null +++ b/notify_state.json @@ -0,0 +1,14 @@ +{ + "彭峰(个人)": { + "date": "2026-03-08", + "hour": 9 + }, + "彭峰(公司)": { + "date": "2026-03-08", + "hour": 9 + }, + "小余": { + "date": "2026-03-08", + "hour": 8 + } +} \ No newline at end of file