修改通知时间为晚上10点,添加cookie保存功能,按用户和域名区分保存
This commit is contained in:
+12
-4
@@ -9,7 +9,8 @@
|
|||||||
},
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
"iyuu_token": "IYUU37629Tc1d371c7ce99a49ff9778e196286b7e4592be191",
|
"iyuu_token": "IYUU37629Tc1d371c7ce99a49ff9778e196286b7e4592be191",
|
||||||
"max_fail_count": 3
|
"max_fail_count": 3,
|
||||||
|
"notify_on_success": true
|
||||||
},
|
},
|
||||||
"browser": {
|
"browser": {
|
||||||
"type": "edge",
|
"type": "edge",
|
||||||
@@ -33,7 +34,8 @@
|
|||||||
},
|
},
|
||||||
"notification": {
|
"notification": {
|
||||||
"iyuu_token": "IYUU37629Tc1d371c7ce99a49ff9778e196286b7e4592be191",
|
"iyuu_token": "IYUU37629Tc1d371c7ce99a49ff9778e196286b7e4592be191",
|
||||||
"max_fail_count": 3
|
"max_fail_count": 3,
|
||||||
|
"notify_on_success": true
|
||||||
},
|
},
|
||||||
"browser": {
|
"browser": {
|
||||||
"type": "edge",
|
"type": "edge",
|
||||||
@@ -44,9 +46,15 @@
|
|||||||
"name": "网站A",
|
"name": "网站A",
|
||||||
"url": "https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll",
|
"url": "https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll",
|
||||||
"login_check_selector": "",
|
"login_check_selector": "",
|
||||||
"success_text": ""
|
"success_text": "彭峰"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "网站B",
|
||||||
|
"url": "https://192.168.1.200:8006/",
|
||||||
|
"login_check_selector": "",
|
||||||
|
"success_text": "彭峰"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-157
@@ -1,160 +1,53 @@
|
|||||||
{
|
{
|
||||||
"cookies": [
|
"用户A": {
|
||||||
{
|
"lmkbi.95155.com": {
|
||||||
"name": "JSESSIONID",
|
"cookies": [],
|
||||||
"value": "DD8A9B4F6E06DF35AE1EC2339DF0FA36",
|
"url": "https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll",
|
||||||
"domain": "10.190.1.205"
|
"website_name": "网站A"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "__rubyUX",
|
|
||||||
"value": "false",
|
|
||||||
"domain": "ntp.msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "pglt-edgeChromium-dhp",
|
|
||||||
"value": "2083",
|
|
||||||
"domain": ".msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "pglt-edgeChromium-ntp",
|
|
||||||
"value": "2083",
|
|
||||||
"domain": ".msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "_C_Auth",
|
|
||||||
"value": "",
|
|
||||||
"domain": "ntp.msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "MicrosoftApplicationsTelemetryDeviceId",
|
|
||||||
"value": "da0bf04e-2115-4952-a012-fc29b4954ec0",
|
|
||||||
"domain": "ntp.msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "USRLOC",
|
|
||||||
"value": "",
|
|
||||||
"domain": ".msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "MUID",
|
|
||||||
"value": "15CB8BCF863F643B03449CDE8778658F",
|
|
||||||
"domain": ".msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "v1",
|
|
||||||
"value": "ExJ_flr=b!>Rb_?o_sb/",
|
|
||||||
"domain": ".mediav.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "MUID",
|
|
||||||
"value": "15CB8BCF863F643B03449CDE8778658F",
|
|
||||||
"domain": ".bing.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "MR",
|
|
||||||
"value": "0",
|
|
||||||
"domain": ".c.bing.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "SRM_B",
|
|
||||||
"value": "15CB8BCF863F643B03449CDE8778658F",
|
|
||||||
"domain": ".c.bing.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "MR",
|
|
||||||
"value": "0",
|
|
||||||
"domain": ".c.msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "MUIDB",
|
|
||||||
"value": "15CB8BCF863F643B03449CDE8778658F",
|
|
||||||
"domain": "ntp.msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": ".ASPXANONYMOUS",
|
|
||||||
"value": "21JPwTAtPyn84bHA6aNjbcWhPsVrLyiIZ-4mnJ6_dMtvLzgXbw3J9nyy0dxCZj0b8vOEHXZpgfYoPylWhp5s3AY2GwcwdYg9JI6VqZ6YK3uRkwMf0",
|
|
||||||
"domain": "i3.sinopec.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "x",
|
|
||||||
"value": "x",
|
|
||||||
"domain": "auth.siam.sinopec.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "msnup",
|
|
||||||
"value": "%7B%22cnex%22%3A%22no%22%7D",
|
|
||||||
"domain": ".msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "msaoptout",
|
|
||||||
"value": "0",
|
|
||||||
"domain": "ntp.msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "OptanonAlertBoxClosed",
|
|
||||||
"value": "2026-03-04T08:18:01.713Z",
|
|
||||||
"domain": ".msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "eupubconsent-v2",
|
|
||||||
"value": "CQghjcAQghjcAAcABBZHBfFsAP_gAELgACiQK4tX_G__bXlr8X73aftkeY1f99h77sQxBhaJk-4FzJvW_JwX32E7NAz6tqYKmRIAu3TBAQNlHJDURVCgaIgVqSDMaEyUoTNKJ6BkiFMRY2dYCFxvm4tjeQCY5vr991d52B-t7dr83dzyy4hHv3a5_0S1WAAAAYCNDfv9bROb-9IO9_x8v4v4_F7pE2_eS1l_tWvp7D9-cts_9XW99_bbff9Pn_-uF_-_X_vf_H37v9oK5AACAAAARgkCAAAgAAAAABAAAAAQAAAAAQBgAAARBAEAAgAQGEgAEAAAAIAAAAAgSAAAAAJAAgAAAABQAAAIBAAAAwAIBgAAGAAACABACAAAAgEgYJgQQCBAABCYBAAgQgAAAACwECAABAgiACEQcAQAAAAAAAAEAAoAAEAgDAQkkBCxIIAuIJoAACAAAIIAChFJ2YAggDNlqrwZNoytMCwfMFzymAZAEQRk5JsQmsAoJA1AAQAAuACgAKgAcAA8ACCAGQAagA8ACIAEwAKoAbwA9AB-AEJAIYAiQBHACWAE0AK0AYYAywBsgDvgHsAfEA-wD9AIBARcBGACNAFBAKgAVcAuYBigDaAG4AOIAkQBOwChwFHgKRAWwAuQBd4DDQGSAMnAZcAzmBrAGsgNvAeOEAMAAOAHOAQcAn4CPQEigJWATaAsIBeQDEAGLQMhAyMBowDUwG0ANuAboA8oB8gD9wICAQMAgiOATgAIgAcAB4AFwAfgBoAHOAO4AgEBBwEIAJ-AVAAvQB0gEegJFASsAmIBMoCbQFIAKTAWoAvoBiADFgGQgMmAaMA00BqYDXgG0ANuAeUA-IB9sD9gP3AgeBBEdBAAAXABQAFQAOAAgABdADIANQAeABEACYAFWALgAugBiADeAHoAP0AhgCJAEsAJoAUYArQBhgDKAGiANkAd4A9oB9gH7ARYBGACggFXALEAXMAvIBigDaAG4AOIAdQBF4CRAEyAJ2AUOAo8BTQCxQFsALgAXIAu0Bd4DDQGPAMkAZOAyqBlgGXAM5AaqA1gBt4DxwH1gQBIAHAAEABoAHOAWIBB4CPQE2gKTAVKAvIBqYDbAG3APKAfEA_YCB4EGAINgQrAimBGkCN8EkQSSAqmBWkCtsFcQVyIQIwAFgAUABcAFUALgAYgA3gB6AEcAO8AigBKQCggFXALmAYoA2gB1IFNAU2AqwBYoC0QFwALkAZOAzkBqoDxwH9gQtAh6BIoCSAFSgKvkoD4ACAAFgAUAA4ADwAIgATAAqgBcADFAIYAiQBHACjAFaANkAd4A_ACrgGKAOoAi8BIgCjwFigLYAZOAywBnIDWAG3gQPJADQALgDuAIAAVABHoCRQErAJtAUmAxYB5QD9wIIlIFwAC4AKAAqABwAEEAMgA0AB4AEQAJgAVQAxAB-gEMARIAowBWgDKAGiANkAd8A-wD9AIsARgAoIBVwC5gF5AMUAbQA3ACLwEiAJ2AUOApoBYoC2AFwALkAXaAw0BkgDJwGXAM5gawBrIDbwHjlADYAFwBHADnAHcAQAAkQBYgDXgHbAP-Aj0BIoCYgE2gKQAU-AvIBfQDFgGTANTAa8A8oB8UD9gP3AgYBA8.f_wACFwAAAAA",
|
|
||||||
"domain": ".msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "MSFPC",
|
|
||||||
"value": "GUID=139384d4545d4f4583ff3ef161faee39&HASH=1393&LV=202603&V=4&LU=1772612281994",
|
|
||||||
"domain": "ntp.msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "UID",
|
|
||||||
"value": "1862498edfbe5a24e12c8d21772613043",
|
|
||||||
"domain": ".scorecardresearch.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "XID",
|
|
||||||
"value": "1862498edfbe5a24e12c8d21772613043",
|
|
||||||
"domain": ".scorecardresearch.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "SM",
|
|
||||||
"value": "C",
|
|
||||||
"domain": ".c.msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "JSESSIONID",
|
|
||||||
"value": "DD8A9B4F6E06DF35AE1EC2339DF0FA36",
|
|
||||||
"domain": "10.190.1.205"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "language",
|
|
||||||
"value": "zh-CN",
|
|
||||||
"domain": "192.168.8.147"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "fnos-token",
|
|
||||||
"value": "n4fYErf2p2lHSucAt8uYAo+M+JDy354smCjF98dHtJM=",
|
|
||||||
"domain": "192.168.8.147"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "fnos-long-token",
|
|
||||||
"value": "07MRUOgDAAC3g89pAAAAAMrPwB8hdcC8nqwi0bqBWfNDO0uPBM7fPQ==",
|
|
||||||
"domain": "192.168.8.147"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "_C_ETH",
|
|
||||||
"value": "1",
|
|
||||||
"domain": ".msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "_EDGE_S",
|
|
||||||
"value": "\"SID=217608637BE1667B39501F717A966764\"",
|
|
||||||
"domain": ".msn.cn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "OptanonConsent",
|
|
||||||
"value": "isGpcEnabled=0&datestamp=Wed+Mar+04+2026+17%3A09%3A17+GMT%2B0800+(%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4)&version=202501.2.0&browserGpcFlag=0&isIABGlobal=false&hosts=&consentId=3599d387-f02a-409c-910d-0a6caea03c05&interactionCount=1&isAnonUser=1&landingPath=NotLandingPage&groups=C0001%3A1%2CC0002%3A1%2CC0004%3A1%2CC0008%3A1%2CV2STACK42%3A1&AwaitingReconsent=false&intType=1&geolocation=%3B",
|
|
||||||
"domain": ".msn.cn"
|
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"url": "http://192.168.8.147:5666/login"
|
"用户B": {
|
||||||
|
"lmkbi.95155.com": {
|
||||||
|
"cookies": [
|
||||||
|
{
|
||||||
|
"name": "_qimei_uuid42",
|
||||||
|
"value": "1a302130f16100ca18af370a47cb8afce807c4a076",
|
||||||
|
"domain": ".95155.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_qimei_fingerprint",
|
||||||
|
"value": "50aa79f919511142818459f44ac38dee",
|
||||||
|
"domain": ".95155.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_qimei_i_3",
|
||||||
|
"value": "64c844d0c35e05dc9796fc355d8470e5a3eba5f0410e00d3e78c2d0e2f95293d306031943c89e29eb6a6",
|
||||||
|
"domain": ".95155.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_qimei_h38",
|
||||||
|
"value": "f8e0750618af370a47cb8afc0200000c91a302",
|
||||||
|
"domain": ".95155.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "_qimei_i_1",
|
||||||
|
"value": "42d946d49d0f50dcc497f83153d525e3f0eef5f51508518ae5d97b582493206c6163649c39d8e1dcd4a9dffb",
|
||||||
|
"domain": ".95155.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"url": "https://lmkbi.95155.com/bi-system/#/carUnpetrifiedComAll",
|
||||||
|
"website_name": "网站A"
|
||||||
|
},
|
||||||
|
"192.168.1.200:8006": {
|
||||||
|
"cookies": [
|
||||||
|
{
|
||||||
|
"name": "PVELangCookie",
|
||||||
|
"value": "zh_CN",
|
||||||
|
"domain": "192.168.1.200"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"url": "https://192.168.1.200:8006/",
|
||||||
|
"website_name": "网站B"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+73
-49
@@ -4,7 +4,7 @@ from tkinter import ttk, messagebox
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
from DrissionPage import Chromium, ChromiumOptions
|
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
@@ -25,8 +25,7 @@ class CookieExtractorApp:
|
|||||||
self.root.geometry("600x250")
|
self.root.geometry("600x250")
|
||||||
self.root.resizable(True, True)
|
self.root.resizable(True, True)
|
||||||
|
|
||||||
self.browser = None
|
self.page = None
|
||||||
self.tab = None
|
|
||||||
|
|
||||||
self._create_widgets()
|
self._create_widgets()
|
||||||
|
|
||||||
@@ -64,6 +63,18 @@ class CookieExtractorApp:
|
|||||||
ttk.Label(status_frame, text="状态:").pack(side=tk.LEFT, padx=5)
|
ttk.Label(status_frame, text="状态:").pack(side=tk.LEFT, padx=5)
|
||||||
ttk.Label(status_frame, textvariable=self.status_var, foreground="blue").pack(side=tk.LEFT, padx=5)
|
ttk.Label(status_frame, textvariable=self.status_var, foreground="blue").pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
def _init_browser(self):
|
||||||
|
"""初始化浏览器"""
|
||||||
|
co = ChromiumOptions()
|
||||||
|
co.set_browser_path(r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe")
|
||||||
|
self.page = ChromiumPage(addr_or_opts=co)
|
||||||
|
|
||||||
|
def _wait_page_loaded(self, timeout=30):
|
||||||
|
"""等待页面加载完成"""
|
||||||
|
if self.page:
|
||||||
|
self.page.wait.doc_loaded(timeout=timeout)
|
||||||
|
self.page.ele('tag:body', timeout=timeout)
|
||||||
|
|
||||||
def open_website(self):
|
def open_website(self):
|
||||||
"""打开指定网站"""
|
"""打开指定网站"""
|
||||||
url = self.url_var.get().strip()
|
url = self.url_var.get().strip()
|
||||||
@@ -82,24 +93,13 @@ class CookieExtractorApp:
|
|||||||
self.status_var.set("正在打开浏览器...")
|
self.status_var.set("正在打开浏览器...")
|
||||||
self.root.update()
|
self.root.update()
|
||||||
|
|
||||||
co = ChromiumOptions()
|
self._init_browser()
|
||||||
co.set_browser_path(r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe")
|
|
||||||
co.set_argument('--no-sandbox')
|
|
||||||
co.set_argument('--disable-dev-shm-usage')
|
|
||||||
|
|
||||||
self.browser = Chromium(co)
|
|
||||||
self.tab = self.browser.new_tab()
|
|
||||||
|
|
||||||
self.status_var.set(f"正在访问: {url}")
|
self.status_var.set(f"正在访问: {url}")
|
||||||
self.root.update()
|
self.root.update()
|
||||||
|
|
||||||
self.tab.get(url)
|
self.page.get(url)
|
||||||
|
self._wait_page_loaded()
|
||||||
self.status_var.set("等待页面加载...")
|
|
||||||
self.root.update()
|
|
||||||
|
|
||||||
self.tab.wait.load_start()
|
|
||||||
self.tab.wait.doc_loaded()
|
|
||||||
|
|
||||||
self.status_var.set(f"已打开: {url}")
|
self.status_var.set(f"已打开: {url}")
|
||||||
self.save_button.config(state=tk.NORMAL)
|
self.save_button.config(state=tk.NORMAL)
|
||||||
@@ -113,16 +113,15 @@ class CookieExtractorApp:
|
|||||||
|
|
||||||
def save_cookies(self):
|
def save_cookies(self):
|
||||||
"""保存当前的cookie信息,并强制关闭浏览器"""
|
"""保存当前的cookie信息,并强制关闭浏览器"""
|
||||||
if not self.browser or not self.tab:
|
if not self.page:
|
||||||
messagebox.showerror("错误", "请先打开网站")
|
messagebox.showerror("错误", "请先打开网站")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.status_var.set("正在提取cookies...")
|
self.status_var.set("正在保存Cookies...")
|
||||||
self.root.update()
|
self.root.update()
|
||||||
|
|
||||||
cookies = self.browser.cookies()
|
cookies = self.page.cookies()
|
||||||
logger.info(f"成功提取 {len(cookies)} 个 cookies")
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"cookies": cookies,
|
"cookies": cookies,
|
||||||
@@ -133,13 +132,12 @@ class CookieExtractorApp:
|
|||||||
with open(output_file, 'w', encoding='utf-8') as f:
|
with open(output_file, 'w', encoding='utf-8') as f:
|
||||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
self.status_var.set("正在关闭浏览器...")
|
self.page.quit()
|
||||||
self.root.update()
|
self.page = None
|
||||||
|
self.save_button.config(state=tk.DISABLED)
|
||||||
|
|
||||||
self._cleanup_browser()
|
self.status_var.set(f"Cookies已保存到 {output_file}")
|
||||||
|
messagebox.showinfo('成功', 'Cookies保存成功,浏览器已关闭')
|
||||||
self.status_var.set(f"数据已保存到: {output_file}")
|
|
||||||
messagebox.showinfo("成功", f"已成功保存 {len(cookies)} 个cookie到: {output_file}\n\n浏览器已关闭")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"保存cookies失败: {e}")
|
logger.error(f"保存cookies失败: {e}")
|
||||||
@@ -148,7 +146,8 @@ class CookieExtractorApp:
|
|||||||
self._cleanup_browser()
|
self._cleanup_browser()
|
||||||
|
|
||||||
def import_cookies(self):
|
def import_cookies(self):
|
||||||
"""打开浏览器,导入cookie信息,然后打开指定网站"""
|
"""打开浏览器,导入cookie和localStorage信息,然后打开指定网站"""
|
||||||
|
import time
|
||||||
try:
|
try:
|
||||||
output_file = "cookie_data.json"
|
output_file = "cookie_data.json"
|
||||||
if not os.path.exists(output_file):
|
if not os.path.exists(output_file):
|
||||||
@@ -163,6 +162,7 @@ class CookieExtractorApp:
|
|||||||
|
|
||||||
url = data.get("url", "")
|
url = data.get("url", "")
|
||||||
cookies = data.get("cookies", [])
|
cookies = data.get("cookies", [])
|
||||||
|
local_storage = data.get("local_storage", {})
|
||||||
|
|
||||||
if not url:
|
if not url:
|
||||||
url = self.url_var.get().strip()
|
url = self.url_var.get().strip()
|
||||||
@@ -179,17 +179,16 @@ class CookieExtractorApp:
|
|||||||
self.status_var.set("正在打开浏览器...")
|
self.status_var.set("正在打开浏览器...")
|
||||||
self.root.update()
|
self.root.update()
|
||||||
|
|
||||||
co = ChromiumOptions()
|
self._init_browser()
|
||||||
co.set_browser_path(r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe")
|
|
||||||
co.set_argument('--no-sandbox')
|
|
||||||
co.set_argument('--disable-dev-shm-usage')
|
|
||||||
|
|
||||||
self.browser = Chromium(co)
|
self.status_var.set("等待浏览器就绪...")
|
||||||
self.tab = self.browser.new_tab()
|
|
||||||
|
|
||||||
self.status_var.set("正在准备环境...")
|
|
||||||
self.root.update()
|
self.root.update()
|
||||||
self.tab.get("about:blank")
|
self.page.get("about:blank")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
parsed = urlparse(url)
|
||||||
|
domain = parsed.netloc
|
||||||
|
|
||||||
if cookies:
|
if cookies:
|
||||||
self.status_var.set("正在导入cookies...")
|
self.status_var.set("正在导入cookies...")
|
||||||
@@ -201,25 +200,51 @@ class CookieExtractorApp:
|
|||||||
if not cookie.get('name') or not cookie.get('value'):
|
if not cookie.get('name') or not cookie.get('value'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
cookie.setdefault('path', '/')
|
cookie_dict = {
|
||||||
cookie.setdefault('httpOnly', False)
|
'name': cookie.get('name'),
|
||||||
cookie.setdefault('secure', False)
|
'value': cookie.get('value'),
|
||||||
|
'domain': cookie.get('domain', domain),
|
||||||
|
'path': cookie.get('path', '/')
|
||||||
|
}
|
||||||
|
|
||||||
self.browser.set.cookies([cookie])
|
self.page.set.cookies(cookie_dict)
|
||||||
success_count += 1
|
success_count += 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"导入cookie失败: {cookie.get('name')} - {e}")
|
logger.error(f"导入cookie失败: {cookie.get('name')} - {e}")
|
||||||
|
|
||||||
logger.info(f"成功导入 {success_count} 个 cookies")
|
logger.info(f"成功导入 {success_count} 个 cookies")
|
||||||
|
|
||||||
|
if local_storage:
|
||||||
|
self.status_var.set("正在导入localStorage...")
|
||||||
|
self.root.update()
|
||||||
|
|
||||||
|
ls_count = 0
|
||||||
|
for ls_domain, items in local_storage.items():
|
||||||
|
try:
|
||||||
|
for key, value in items.items():
|
||||||
|
try:
|
||||||
|
self.page.set.local_storage(key, value)
|
||||||
|
ls_count += 1
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"导入localStorage失败: {key} - {e}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"导入localStorage域失败: {ls_domain} - {e}")
|
||||||
|
|
||||||
|
logger.info(f"成功导入 {ls_count} 个 localStorage")
|
||||||
|
|
||||||
self.status_var.set(f"正在访问: {url}")
|
self.status_var.set(f"正在访问: {url}")
|
||||||
self.root.update()
|
self.root.update()
|
||||||
self.tab.get(url)
|
self.page.get(url)
|
||||||
|
self._wait_page_loaded()
|
||||||
|
|
||||||
self.status_var.set("等待页面加载...")
|
self.status_var.set("刷新页面验证登录状态...")
|
||||||
self.root.update()
|
self.root.update()
|
||||||
self.tab.wait.load_start()
|
for i in range(3):
|
||||||
self.tab.wait.doc_loaded()
|
self.page.refresh()
|
||||||
|
self._wait_page_loaded()
|
||||||
|
time.sleep(1)
|
||||||
|
self.status_var.set(f"刷新第 {i+1} 次...")
|
||||||
|
self.root.update()
|
||||||
|
|
||||||
self.url_var.set(url)
|
self.url_var.set(url)
|
||||||
self.status_var.set(f"已导入并打开: {url}")
|
self.status_var.set(f"已导入并打开: {url}")
|
||||||
@@ -237,14 +262,13 @@ class CookieExtractorApp:
|
|||||||
self.root.destroy()
|
self.root.destroy()
|
||||||
|
|
||||||
def _cleanup_browser(self):
|
def _cleanup_browser(self):
|
||||||
if self.browser:
|
if self.page:
|
||||||
try:
|
try:
|
||||||
self.browser.quit()
|
self.page.quit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"关闭浏览器失败: {e}")
|
logger.error(f"关闭浏览器失败: {e}")
|
||||||
finally:
|
finally:
|
||||||
self.browser = None
|
self.page = None
|
||||||
self.tab = None
|
|
||||||
self.save_button.config(state=tk.DISABLED)
|
self.save_button.config(state=tk.DISABLED)
|
||||||
self.status_var.set("就绪")
|
self.status_var.set("就绪")
|
||||||
|
|
||||||
|
|||||||
+300
-52
@@ -1,13 +1,166 @@
|
|||||||
"""Cookie 监控主程序"""
|
"""Cookie 监控主程序"""
|
||||||
import json
|
import json
|
||||||
from datetime import datetime
|
import time
|
||||||
|
import os
|
||||||
|
from datetime import datetime, time as dt_time
|
||||||
|
from urllib.parse import urlparse
|
||||||
from cookie_cloud import CookieCloud
|
from cookie_cloud import CookieCloud
|
||||||
from notifier import IYUUNotifier, FailureTracker
|
from notifier import IYUUNotifier
|
||||||
from browser_login import BrowserLogin, CookieManager
|
|
||||||
from applogger import setup_logging, get_logger
|
from applogger import setup_logging, get_logger
|
||||||
|
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
NOTIFY_TIMES = [6, 22]
|
||||||
|
STATE_FILE = "notify_state.json"
|
||||||
|
|
||||||
|
|
||||||
|
def load_notify_state() -> dict:
|
||||||
|
if os.path.exists(STATE_FILE):
|
||||||
|
try:
|
||||||
|
with open(STATE_FILE, 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def save_notify_state(state: dict):
|
||||||
|
try:
|
||||||
|
with open(STATE_FILE, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(state, f, indent=2, ensure_ascii=False)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"保存通知状态失败: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def should_send_notification(user_name: str) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def mark_notification_sent(user_name: str):
|
||||||
|
now = datetime.now()
|
||||||
|
state = load_notify_state()
|
||||||
|
|
||||||
|
if user_name not in state:
|
||||||
|
state[user_name] = {}
|
||||||
|
|
||||||
|
state[user_name]['date'] = now.strftime('%Y-%m-%d')
|
||||||
|
state[user_name]['hour'] = now.hour
|
||||||
|
|
||||||
|
save_notify_state(state)
|
||||||
|
|
||||||
|
|
||||||
|
class BrowserManager:
|
||||||
|
"""浏览器管理器"""
|
||||||
|
|
||||||
|
def __init__(self, browser_type: str = 'edge', headless: bool = False):
|
||||||
|
self.browser_type = browser_type
|
||||||
|
self.headless = headless
|
||||||
|
self.page = None
|
||||||
|
|
||||||
|
def init_browser(self):
|
||||||
|
"""初始化浏览器"""
|
||||||
|
co = ChromiumOptions()
|
||||||
|
if self.browser_type == 'edge':
|
||||||
|
co.set_browser_path(r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe")
|
||||||
|
co.set_argument('--no-sandbox')
|
||||||
|
co.set_argument('--disable-dev-shm-usage')
|
||||||
|
if self.headless:
|
||||||
|
co.set_argument('--headless')
|
||||||
|
self.page = ChromiumPage(addr_or_opts=co)
|
||||||
|
|
||||||
|
def wait_page_loaded(self, timeout=30):
|
||||||
|
"""等待页面加载完成"""
|
||||||
|
if self.page:
|
||||||
|
self.page.wait.doc_loaded(timeout=timeout)
|
||||||
|
self.page.ele('tag:body', timeout=timeout)
|
||||||
|
|
||||||
|
def ensure_connection(self):
|
||||||
|
"""确保浏览器连接正常"""
|
||||||
|
try:
|
||||||
|
if self.page:
|
||||||
|
self.page.get("about:blank")
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.init_browser()
|
||||||
|
self.page.get("about:blank")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"无法建立浏览器连接: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def import_cookies(self, cookies: list, domain: str):
|
||||||
|
"""导入cookies"""
|
||||||
|
if not self.ensure_connection():
|
||||||
|
return 0
|
||||||
|
|
||||||
|
success_count = 0
|
||||||
|
for cookie in cookies:
|
||||||
|
try:
|
||||||
|
if not cookie.get('name') or not cookie.get('value'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
cookie_dict = {
|
||||||
|
'name': cookie.get('name'),
|
||||||
|
'value': cookie.get('value'),
|
||||||
|
'domain': cookie.get('domain', domain),
|
||||||
|
'path': cookie.get('path', '/')
|
||||||
|
}
|
||||||
|
|
||||||
|
self.page.set.cookies(cookie_dict)
|
||||||
|
success_count += 1
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return success_count
|
||||||
|
|
||||||
|
def verify_login(self, url: str, check_selector: str = "", success_text: str = "") -> bool:
|
||||||
|
"""验证登录状态"""
|
||||||
|
if not self.ensure_connection():
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.page.get(url)
|
||||||
|
self.wait_page_loaded()
|
||||||
|
|
||||||
|
for i in range(3):
|
||||||
|
time.sleep(1)
|
||||||
|
self.page.refresh()
|
||||||
|
self.wait_page_loaded()
|
||||||
|
|
||||||
|
if not check_selector:
|
||||||
|
return True
|
||||||
|
|
||||||
|
try:
|
||||||
|
element = self.page.ele(check_selector, timeout=10)
|
||||||
|
if element:
|
||||||
|
element_text = element.text or ""
|
||||||
|
if not success_text:
|
||||||
|
return True
|
||||||
|
if success_text in element_text:
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"验证登录状态失败: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""关闭浏览器"""
|
||||||
|
if self.page:
|
||||||
|
try:
|
||||||
|
self.page.quit()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
self.page = None
|
||||||
|
|
||||||
|
|
||||||
class CookieMonitor:
|
class CookieMonitor:
|
||||||
"""Cookie 监控器"""
|
"""Cookie 监控器"""
|
||||||
@@ -15,8 +168,7 @@ class CookieMonitor:
|
|||||||
def __init__(self, config_file: str = "config.json"):
|
def __init__(self, config_file: str = "config.json"):
|
||||||
self.config_file = config_file
|
self.config_file = config_file
|
||||||
self.config = self._load_config()
|
self.config = self._load_config()
|
||||||
self.cookie_manager = CookieManager("cookies.json")
|
self.all_results = []
|
||||||
self.failure_tracker = FailureTracker("state.json")
|
|
||||||
|
|
||||||
def _load_config(self) -> dict:
|
def _load_config(self) -> dict:
|
||||||
try:
|
try:
|
||||||
@@ -26,14 +178,8 @@ class CookieMonitor:
|
|||||||
logger.error(f"加载配置文件失败: {e}")
|
logger.error(f"加载配置文件失败: {e}")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def _get_domain_from_url(self, url: str) -> str:
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
return urlparse(url).netloc
|
|
||||||
|
|
||||||
def process_user(self, user_config: dict):
|
def process_user(self, user_config: dict):
|
||||||
"""
|
"""处理单个用户:依次验证各网站登录状态"""
|
||||||
处理单个用户:一次性导入所有cookies,然后验证各网站登录状态
|
|
||||||
"""
|
|
||||||
user_name = user_config.get('name', '未知用户')
|
user_name = user_config.get('name', '未知用户')
|
||||||
logger.info(f"{'='*20} 开始处理用户: {user_name} {'='*20}")
|
logger.info(f"{'='*20} 开始处理用户: {user_name} {'='*20}")
|
||||||
|
|
||||||
@@ -46,10 +192,6 @@ class CookieMonitor:
|
|||||||
logger.info(f"用户 {user_name} 没有配置网站,跳过")
|
logger.info(f"用户 {user_name} 没有配置网站,跳过")
|
||||||
return
|
return
|
||||||
|
|
||||||
iyuu_token = notification_config.get('iyuu_token', '')
|
|
||||||
max_fail_count = notification_config.get('max_fail_count', 3)
|
|
||||||
notifier = IYUUNotifier(iyuu_token) if iyuu_token else None
|
|
||||||
|
|
||||||
cookie_cloud = CookieCloud(
|
cookie_cloud = CookieCloud(
|
||||||
api_url=cookie_cloud_config.get('api_url', ''),
|
api_url=cookie_cloud_config.get('api_url', ''),
|
||||||
uuid=cookie_cloud_config.get('uuid', ''),
|
uuid=cookie_cloud_config.get('uuid', ''),
|
||||||
@@ -62,46 +204,44 @@ class CookieMonitor:
|
|||||||
total_cookies = sum(len(c) for c in all_cookies.values())
|
total_cookies = sum(len(c) for c in all_cookies.values())
|
||||||
logger.info(f"获取到 {total_domains} 个域名,共 {total_cookies} 个 cookie")
|
logger.info(f"获取到 {total_domains} 个域名,共 {total_cookies} 个 cookie")
|
||||||
|
|
||||||
browser = BrowserLogin(
|
browser = BrowserManager(
|
||||||
browser_type=browser_config.get('type', 'edge'),
|
browser_type=browser_config.get('type', 'edge'),
|
||||||
headless=browser_config.get('headless', False)
|
headless=browser_config.get('headless', False)
|
||||||
)
|
)
|
||||||
|
|
||||||
results = []
|
user_results = {
|
||||||
|
'user_name': user_name,
|
||||||
|
'notification_config': notification_config,
|
||||||
|
'websites': [],
|
||||||
|
'success_count': 0,
|
||||||
|
'fail_count': 0
|
||||||
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"步骤2: 一次性导入所有 cookies 到浏览器")
|
logger.info(f"步骤2: 初始化浏览器")
|
||||||
all_cookies_list = []
|
browser.init_browser()
|
||||||
for cookies in all_cookies.values():
|
|
||||||
all_cookies_list.extend(cookies)
|
|
||||||
|
|
||||||
import_success = browser.import_all_cookies(all_cookies_list)
|
|
||||||
if import_success:
|
|
||||||
logger.info(f"成功导入 {len(all_cookies_list)} 个 cookie")
|
|
||||||
else:
|
|
||||||
logger.warning(f"部分 cookie 导入失败")
|
|
||||||
|
|
||||||
logger.info(f"步骤3: 依次验证各网站登录状态")
|
logger.info(f"步骤3: 依次验证各网站登录状态")
|
||||||
for website in websites:
|
for website in websites:
|
||||||
result = self.process_website(
|
result = self.process_website(
|
||||||
user_name=user_name,
|
user_name=user_name,
|
||||||
website_config=website,
|
website_config=website,
|
||||||
browser=browser
|
browser=browser,
|
||||||
|
all_cookies=all_cookies
|
||||||
)
|
)
|
||||||
results.append(result)
|
user_results['websites'].append(result)
|
||||||
|
|
||||||
if not result['success']:
|
if result['success']:
|
||||||
self.failure_tracker.check_and_notify(
|
user_results['success_count'] += 1
|
||||||
user_name=user_name,
|
|
||||||
website_name=result['name'],
|
|
||||||
max_fail_count=max_fail_count,
|
|
||||||
notifier=notifier,
|
|
||||||
error_msg=result['error']
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.failure_tracker.reset_fail_count(user_name, result['name'])
|
user_results['fail_count'] += 1
|
||||||
|
|
||||||
self._print_summary(user_name, results)
|
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)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
browser.close()
|
browser.close()
|
||||||
@@ -110,14 +250,10 @@ class CookieMonitor:
|
|||||||
self,
|
self,
|
||||||
user_name: str,
|
user_name: str,
|
||||||
website_config: dict,
|
website_config: dict,
|
||||||
browser: BrowserLogin
|
browser: BrowserManager,
|
||||||
|
all_cookies: dict
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""
|
"""验证网站登录状态"""
|
||||||
验证网站登录状态(cookies已预先导入)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: {'name': 网站名, 'url': URL, 'success': 是否成功, 'error': 错误信息}
|
|
||||||
"""
|
|
||||||
website_name = website_config.get('name', '未知网站')
|
website_name = website_config.get('name', '未知网站')
|
||||||
url = website_config.get('url', '')
|
url = website_config.get('url', '')
|
||||||
check_selector = website_config.get('login_check_selector', '')
|
check_selector = website_config.get('login_check_selector', '')
|
||||||
@@ -139,6 +275,22 @@ class CookieMonitor:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
parsed = urlparse(url)
|
||||||
|
domain = parsed.netloc
|
||||||
|
|
||||||
|
domain_cookies = []
|
||||||
|
for cookie_domain, cookies in all_cookies.items():
|
||||||
|
clean_domain = cookie_domain.lstrip('.')
|
||||||
|
if domain in clean_domain or clean_domain in domain:
|
||||||
|
domain_cookies.extend(cookies)
|
||||||
|
|
||||||
|
if domain_cookies:
|
||||||
|
logger.info(f" 找到 {len(domain_cookies)} 个相关 cookie")
|
||||||
|
success_count = browser.import_cookies(domain_cookies, domain)
|
||||||
|
logger.info(f" 成功导入 {success_count} 个 cookie")
|
||||||
|
else:
|
||||||
|
logger.warning(f" 未找到相关 cookie")
|
||||||
|
|
||||||
login_success = browser.verify_login(
|
login_success = browser.verify_login(
|
||||||
url=url,
|
url=url,
|
||||||
check_selector=check_selector,
|
check_selector=check_selector,
|
||||||
@@ -149,10 +301,7 @@ class CookieMonitor:
|
|||||||
result['success'] = True
|
result['success'] = True
|
||||||
logger.info(f" ✓ 登录验证成功")
|
logger.info(f" ✓ 登录验证成功")
|
||||||
|
|
||||||
new_cookies = browser.refresh_and_save_cookies(url)
|
self._save_cookies_to_file(domain_cookies, url, website_name, user_name)
|
||||||
if new_cookies:
|
|
||||||
self.cookie_manager.save_cookies(user_name, website_name, new_cookies)
|
|
||||||
logger.info(f" 已保存 {len(new_cookies)} 个新 cookie")
|
|
||||||
else:
|
else:
|
||||||
result['error'] = '登录状态验证失败(cookies可能已过期或选择器配置错误)'
|
result['error'] = '登录状态验证失败(cookies可能已过期或选择器配置错误)'
|
||||||
logger.warning(f" ✗ {result['error']}")
|
logger.warning(f" ✗ {result['error']}")
|
||||||
@@ -163,6 +312,49 @@ class CookieMonitor:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _save_cookies_to_file(self, cookies: list, url: str, website_name: str, user_name: str):
|
||||||
|
"""保存 cookies 到文件(按用户和域名区分)"""
|
||||||
|
try:
|
||||||
|
parsed = urlparse(url)
|
||||||
|
domain = parsed.netloc
|
||||||
|
|
||||||
|
cookie_entry = {
|
||||||
|
"cookies": [],
|
||||||
|
"url": url,
|
||||||
|
"website_name": website_name
|
||||||
|
}
|
||||||
|
|
||||||
|
for cookie in cookies:
|
||||||
|
if cookie.get('name') and cookie.get('value'):
|
||||||
|
cookie_entry["cookies"].append({
|
||||||
|
"name": cookie.get('name'),
|
||||||
|
"value": cookie.get('value'),
|
||||||
|
"domain": cookie.get('domain', domain)
|
||||||
|
})
|
||||||
|
|
||||||
|
filename = "cookie_data.json"
|
||||||
|
|
||||||
|
cookie_data = {}
|
||||||
|
if os.path.exists(filename):
|
||||||
|
with open(filename, 'r', encoding='utf-8') as f:
|
||||||
|
try:
|
||||||
|
cookie_data = json.load(f)
|
||||||
|
except:
|
||||||
|
cookie_data = {}
|
||||||
|
|
||||||
|
if user_name not in cookie_data:
|
||||||
|
cookie_data[user_name] = {}
|
||||||
|
|
||||||
|
cookie_data[user_name][domain] = cookie_entry
|
||||||
|
|
||||||
|
with open(filename, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(cookie_data, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
logger.info(f" 已保存 {len(cookie_entry['cookies'])} 个 cookie 到 {filename} ({user_name} -> {domain})")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f" 保存 cookie 失败: {e}")
|
||||||
|
|
||||||
def _print_summary(self, user_name: str, results: list):
|
def _print_summary(self, user_name: str, results: list):
|
||||||
"""打印处理结果汇总"""
|
"""打印处理结果汇总"""
|
||||||
success_count = sum(1 for r in results if r['success'])
|
success_count = sum(1 for r in results if r['success'])
|
||||||
@@ -178,6 +370,59 @@ class CookieMonitor:
|
|||||||
if not r['success']:
|
if not r['success']:
|
||||||
logger.info(f" - {r['name']}: {r['error']}")
|
logger.info(f" - {r['name']}: {r['error']}")
|
||||||
|
|
||||||
|
def _send_user_notification(self, user_result: dict):
|
||||||
|
"""发送单个用户的通知"""
|
||||||
|
user_name = user_result['user_name']
|
||||||
|
notification_config = user_result.get('notification_config', {})
|
||||||
|
iyuu_token = notification_config.get('iyuu_token', '')
|
||||||
|
|
||||||
|
if not iyuu_token:
|
||||||
|
return
|
||||||
|
|
||||||
|
notifier = IYUUNotifier(iyuu_token)
|
||||||
|
|
||||||
|
success_count = user_result['success_count']
|
||||||
|
fail_count = user_result['fail_count']
|
||||||
|
|
||||||
|
title = f"【Cookie监控】{user_name} - 成功{success_count} 失败{fail_count}"
|
||||||
|
|
||||||
|
content_lines = [
|
||||||
|
f"**{user_name} Cookie监控报告**",
|
||||||
|
f"",
|
||||||
|
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}")
|
||||||
|
|
||||||
|
content = "\n".join(content_lines)
|
||||||
|
|
||||||
|
print(f"\n{'='*60}")
|
||||||
|
print(f"发送用户通知: {user_name}")
|
||||||
|
print(f"{'='*60}")
|
||||||
|
print(f"\n发送内容:")
|
||||||
|
print(f"Title: {title}")
|
||||||
|
print(f"Content:\n{content}")
|
||||||
|
print(f"\n发送方式: 爱语飞飞 API (GET 请求)")
|
||||||
|
print(f"API URL: https://iyuu.cn/{iyuu_token}.send")
|
||||||
|
print(f"参数: text={title}, desp={content[:50]}...")
|
||||||
|
print(f"\n正在发送...")
|
||||||
|
|
||||||
|
logger.info(f"\n发送用户通知: {user_name}")
|
||||||
|
logger.info(f"Title: {title}")
|
||||||
|
|
||||||
|
if notifier.send(title, content):
|
||||||
|
print(f"\n发送结果: ✓ 成功")
|
||||||
|
logger.info(f"✓ 用户 {user_name} 通知发送成功")
|
||||||
|
mark_notification_sent(user_name)
|
||||||
|
else:
|
||||||
|
print(f"\n发送结果: ✗ 失败")
|
||||||
|
logger.error(f"✗ 用户 {user_name} 通知发送失败")
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""运行监控"""
|
"""运行监控"""
|
||||||
logger.info("="*50)
|
logger.info("="*50)
|
||||||
@@ -197,6 +442,9 @@ class CookieMonitor:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"处理用户失败: {e}")
|
logger.error(f"处理用户失败: {e}")
|
||||||
|
|
||||||
|
for user_result in self.all_results:
|
||||||
|
pass
|
||||||
|
|
||||||
logger.info("\n" + "="*50)
|
logger.info("\n" + "="*50)
|
||||||
logger.info("Cookie 监控结束")
|
logger.info("Cookie 监控结束")
|
||||||
logger.info("="*50)
|
logger.info("="*50)
|
||||||
|
|||||||
+15
-150
@@ -1,6 +1,5 @@
|
|||||||
"""消息通知模块 - 通过爱语飞飞发送通知"""
|
"""消息通知模块 - 通过爱语飞飞发送通知"""
|
||||||
import requests
|
import requests
|
||||||
from typing import Optional
|
|
||||||
from applogger import get_logger
|
from applogger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
@@ -10,26 +9,17 @@ class IYUUNotifier:
|
|||||||
"""爱语飞飞通知客户端"""
|
"""爱语飞飞通知客户端"""
|
||||||
|
|
||||||
def __init__(self, token: str):
|
def __init__(self, token: str):
|
||||||
"""
|
|
||||||
初始化爱语飞飞通知客户端
|
|
||||||
|
|
||||||
Args:
|
|
||||||
token: 爱语飞飞令牌
|
|
||||||
"""
|
|
||||||
self.token = token
|
self.token = token
|
||||||
self.api_url = f"https://iyuu.cn/{token}.send"
|
self.api_url = f"https://iyuu.cn/{token}.send"
|
||||||
|
|
||||||
def send(self, title: str, content: str) -> bool:
|
def send(self, title: str, content: str) -> bool:
|
||||||
"""
|
print(f"\n{'='*50}")
|
||||||
发送通知
|
print(f"[测试] 准备发送通知")
|
||||||
|
print(f"[测试] API URL: {self.api_url}")
|
||||||
Args:
|
print(f"[测试] Title: {title}")
|
||||||
title: 通知标题
|
print(f"[测试] Content: {content[:100]}...")
|
||||||
content: 通知内容
|
print(f"{'='*50}\n")
|
||||||
|
|
||||||
Returns:
|
|
||||||
是否发送成功
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
self.api_url,
|
self.api_url,
|
||||||
@@ -39,12 +29,20 @@ class IYUUNotifier:
|
|||||||
},
|
},
|
||||||
timeout=10
|
timeout=10
|
||||||
)
|
)
|
||||||
|
|
||||||
|
print(f"[测试] 响应状态码: {response.status_code}")
|
||||||
|
print(f"[测试] 响应内容: {response.text}")
|
||||||
|
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
result = response.json()
|
result = response.json()
|
||||||
|
|
||||||
|
print(f"[测试] 解析结果: {result}")
|
||||||
|
|
||||||
if result.get('errcode') == 0:
|
if result.get('errcode') == 0:
|
||||||
|
print(f"[测试] ✓ 发送成功!")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
print(f"[测试] ✗ 发送失败: {result.get('errmsg')}")
|
||||||
logger.error(f"发送通知失败: {result.get('errmsg')}")
|
logger.error(f"发送通知失败: {result.get('errmsg')}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -54,136 +52,3 @@ class IYUUNotifier:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"发送通知异常: {e}")
|
logger.error(f"发送通知异常: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class FailureTracker:
|
|
||||||
"""失败次数追踪器"""
|
|
||||||
|
|
||||||
def __init__(self, state_file: str):
|
|
||||||
"""
|
|
||||||
初始化失败次数追踪器
|
|
||||||
|
|
||||||
Args:
|
|
||||||
state_file: 状态文件路径
|
|
||||||
"""
|
|
||||||
self.state_file = state_file
|
|
||||||
self.state = self._load_state()
|
|
||||||
|
|
||||||
def _load_state(self) -> dict:
|
|
||||||
"""加载状态文件"""
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
|
|
||||||
if os.path.exists(self.state_file):
|
|
||||||
try:
|
|
||||||
with open(self.state_file, 'r', encoding='utf-8') as f:
|
|
||||||
return json.load(f)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"加载状态文件失败: {e}")
|
|
||||||
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def _save_state(self):
|
|
||||||
"""保存状态文件"""
|
|
||||||
import json
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(self.state_file, 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(self.state, f, indent=2, ensure_ascii=False)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"保存状态文件失败: {e}")
|
|
||||||
|
|
||||||
def get_fail_count(self, user_name: str, website_name: str) -> int:
|
|
||||||
"""
|
|
||||||
获取指定网站的失败次数
|
|
||||||
|
|
||||||
Args:
|
|
||||||
user_name: 用户名
|
|
||||||
website_name: 网站名
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
失败次数
|
|
||||||
"""
|
|
||||||
key = f"{user_name}_{website_name}"
|
|
||||||
return self.state.get(key, 0)
|
|
||||||
|
|
||||||
def increment_fail_count(self, user_name: str, website_name: str) -> int:
|
|
||||||
"""
|
|
||||||
增加失败次数
|
|
||||||
|
|
||||||
Args:
|
|
||||||
user_name: 用户名
|
|
||||||
website_name: 网站名
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
增加后的失败次数
|
|
||||||
"""
|
|
||||||
key = f"{user_name}_{website_name}"
|
|
||||||
self.state[key] = self.state.get(key, 0) + 1
|
|
||||||
self._save_state()
|
|
||||||
return self.state[key]
|
|
||||||
|
|
||||||
def reset_fail_count(self, user_name: str, website_name: str):
|
|
||||||
"""
|
|
||||||
重置失败次数
|
|
||||||
|
|
||||||
Args:
|
|
||||||
user_name: 用户名
|
|
||||||
website_name: 网站名
|
|
||||||
"""
|
|
||||||
key = f"{user_name}_{website_name}"
|
|
||||||
self.state[key] = 0
|
|
||||||
self._save_state()
|
|
||||||
|
|
||||||
def check_and_notify(
|
|
||||||
self,
|
|
||||||
user_name: str,
|
|
||||||
website_name: str,
|
|
||||||
max_fail_count: int,
|
|
||||||
notifier: Optional[IYUUNotifier],
|
|
||||||
error_msg: str
|
|
||||||
) -> bool:
|
|
||||||
"""
|
|
||||||
检查失败次数并在达到阈值时发送通知
|
|
||||||
|
|
||||||
Args:
|
|
||||||
user_name: 用户名
|
|
||||||
website_name: 网站名
|
|
||||||
max_fail_count: 最大失败次数
|
|
||||||
notifier: 爱语飞飞通知客户端
|
|
||||||
error_msg: 错误信息
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
是否应该停止重试
|
|
||||||
"""
|
|
||||||
fail_count = self.increment_fail_count(user_name, website_name)
|
|
||||||
|
|
||||||
if fail_count >= max_fail_count:
|
|
||||||
# 发送通知
|
|
||||||
if notifier:
|
|
||||||
title = f"【Cookie监控】{user_name} - {website_name} 登录失败"
|
|
||||||
content = f"网站: {website_name}\n用户: {user_name}\n连续失败次数: {fail_count}\n错误信息: {error_msg}"
|
|
||||||
|
|
||||||
if notifier.send(title, content):
|
|
||||||
logger.info(f"已发送失败通知: {user_name} - {website_name}")
|
|
||||||
else:
|
|
||||||
logger.error(f"发送失败通知失败: {user_name} - {website_name}")
|
|
||||||
|
|
||||||
# 重置失败计数
|
|
||||||
self.reset_fail_count(user_name, website_name)
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# 测试代码
|
|
||||||
tracker = FailureTracker("state.json")
|
|
||||||
|
|
||||||
# 增加失败次数
|
|
||||||
count = tracker.increment_fail_count("测试用户", "测试网站")
|
|
||||||
logger.info(f"当前失败次数: {count}")
|
|
||||||
|
|
||||||
# 检查并发送通知(需要真实的爱语飞飞令牌)
|
|
||||||
# notifier = IYUUNotifier("您的爱语飞飞令牌")
|
|
||||||
# tracker.check_and_notify("测试用户", "测试网站", 3, notifier, "测试错误信息")
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"用户A": {
|
||||||
|
"date": "2026-03-04",
|
||||||
|
"hour": 22
|
||||||
|
},
|
||||||
|
"用户B": {
|
||||||
|
"date": "2026-03-04",
|
||||||
|
"hour": 22
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
"""直接发送测试消息给两个用户"""
|
||||||
|
from notifier import IYUUNotifier
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
iyuu_token = "IYUU37629Tc1d371c7ce99a49ff9778e196286b7e4592be191"
|
||||||
|
notifier = IYUUNotifier(iyuu_token)
|
||||||
|
|
||||||
|
print("="*60)
|
||||||
|
print("发送测试消息给用户A")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
title_a = "【测试】用户A - Cookie监控测试"
|
||||||
|
content_a = f"""**用户A Cookie监控测试报告**
|
||||||
|
|
||||||
|
测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
||||||
|
测试类型: 直接发送测试消息
|
||||||
|
|
||||||
|
这是一条测试消息,用于验证爱语飞飞通知功能是否正常工作。
|
||||||
|
|
||||||
|
如果您收到此消息,说明通知功能正常。"""
|
||||||
|
|
||||||
|
print(f"\n发送内容:")
|
||||||
|
print(f"Title: {title_a}")
|
||||||
|
print(f"Content:\n{content_a}")
|
||||||
|
print(f"\n发送方式: 爱语飞飞 API (GET 请求)")
|
||||||
|
print(f"API URL: https://iyuu.cn/{iyuu_token}.send")
|
||||||
|
print(f"参数: text={title_a}, desp={content_a}")
|
||||||
|
print("\n正在发送...")
|
||||||
|
|
||||||
|
result_a = notifier.send(title_a, content_a)
|
||||||
|
print(f"\n发送结果: {'✓ 成功' if result_a else '✗ 失败'}")
|
||||||
|
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("发送测试消息给用户B")
|
||||||
|
print("="*60)
|
||||||
|
|
||||||
|
title_b = "【测试】用户B - Cookie监控测试"
|
||||||
|
content_b = f"""**用户B Cookie监控测试报告**
|
||||||
|
|
||||||
|
测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
||||||
|
测试类型: 直接发送测试消息
|
||||||
|
|
||||||
|
这是一条测试消息,用于验证爱语飞飞通知功能是否正常工作。
|
||||||
|
|
||||||
|
如果您收到此消息,说明通知功能正常。"""
|
||||||
|
|
||||||
|
print(f"\n发送内容:")
|
||||||
|
print(f"Title: {title_b}")
|
||||||
|
print(f"Content:\n{content_b}")
|
||||||
|
print(f"\n发送方式: 爱语飞飞 API (GET 请求)")
|
||||||
|
print(f"API URL: https://iyuu.cn/{iyuu_token}.send")
|
||||||
|
print(f"参数: text={title_b}, desp={content_b}")
|
||||||
|
print("\n正在发送...")
|
||||||
|
|
||||||
|
result_b = notifier.send(title_b, content_b)
|
||||||
|
print(f"\n发送结果: {'✓ 成功' if result_b else '✗ 失败'}")
|
||||||
|
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("测试完成")
|
||||||
|
print("="*60)
|
||||||
|
print(f"用户A: {'✓ 发送成功' if result_a else '✗ 发送失败'}")
|
||||||
|
print(f"用户B: {'✓ 发送成功' if result_b else '✗ 发送失败'}")
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
"""测试爱语飞飞通知"""
|
||||||
|
import requests
|
||||||
|
|
||||||
|
iyuu_token = "IYUU37629Tc1d371c7ce99a49ff9778e196286b7e4592be191"
|
||||||
|
api_url = f"https://iyuu.cn/{iyuu_token}.send"
|
||||||
|
|
||||||
|
title = "【测试】Cookie监控通知测试"
|
||||||
|
content = """这是一条测试消息。
|
||||||
|
|
||||||
|
测试时间: 2026-03-04
|
||||||
|
测试内容: 验证爱语飞飞通知是否正常工作
|
||||||
|
|
||||||
|
如果您收到此消息,说明通知功能正常。"""
|
||||||
|
|
||||||
|
print(f"API URL: {api_url}")
|
||||||
|
print(f"Title: {title}")
|
||||||
|
print(f"Content: {content}")
|
||||||
|
print("\n正在发送请求...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
api_url,
|
||||||
|
params={
|
||||||
|
'text': title,
|
||||||
|
'desp': content
|
||||||
|
},
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"状态码: {response.status_code}")
|
||||||
|
print(f"响应内容: {response.text}")
|
||||||
|
|
||||||
|
result = response.json()
|
||||||
|
print(f"\n解析结果: {result}")
|
||||||
|
|
||||||
|
if result.get('errcode') == 0:
|
||||||
|
print("\n✓ 发送成功!")
|
||||||
|
else:
|
||||||
|
print(f"\n✗ 发送失败: {result.get('errmsg')}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n✗ 请求异常: {e}")
|
||||||
Reference in New Issue
Block a user