1.4.8-Fix

This commit is contained in:
RachelOS
2025-12-12 18:13:07 +08:00
parent ba5d6738b5
commit 131e0fff76
10 changed files with 204 additions and 64 deletions
+2 -1
View File
@@ -17,4 +17,5 @@ build
dist
data1
doc2pdf/*.docx
doc2pdf/*.pdf
doc2pdf/*.pdf
demo.txt
-4
View File
@@ -2,7 +2,6 @@ import requests
import json
import re
from core.models import Feed
from driver.wx import DoSuccess
from core.db import DB
from core.models.feed import Feed
from .cfg import cfg,wx_cfg
@@ -198,9 +197,6 @@ class WxGather:
print(f"item end")
_cookies=[{'name': c.name, 'value': c.value, 'domain': c.domain,'expiry':c.expires,'expires':c.expires} for c in self._cookies]
_cookies.append({'name':'token','value':self.token})
if len(_cookies) > 0:
# DoSuccess(_cookies)
pass
if CallBack is not None:
CallBack(item)
pass
+10 -2
View File
@@ -1,9 +1,17 @@
from driver.base import WX_API
import threading
from driver.base import WX_InterFace
import os
from core.task import TaskScheduler
from driver.success import Success
def auth():
WX_API.Token(callback=Success)
def run_auth():
wx=WX_InterFace()
wx.Token(callback=Success)
thread = threading.Thread(target=run_auth)
thread.start()
thread.join() # 可选:等待完成
if os.getenv('WE_RSS.AUTH',False):
auth_task=TaskScheduler()
if os.getenv('DEBUG',False):
+3 -1
View File
@@ -4,5 +4,7 @@ from core.config import cfg
print(bool(cfg.get("server.auth_web", False)) )
if bool(cfg.get("server.auth_web", False)) == True:
from driver.wx import WX_API
from driver.wx import Wx as WX_InterFace
else:
from driver.wx_api import WeChat_api as WX_API
from driver.wx_api import WeChat_api as WX_API
from driver.wx_api import WeChatAPI as WX_InterFace
+4 -9
View File
@@ -42,7 +42,9 @@ class PlaywrightController:
def is_async(self):
try:
# 尝试获取事件循环
loop = asyncio.get_running_loop()
# 设置合适的事件循环策略
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return True
except RuntimeError:
# 如果没有正在运行的事件循环,则说明不是异步环境
@@ -54,14 +56,7 @@ class PlaywrightController:
headless = False
if self.driver is None:
# 修复所有操作系统下的异步子进程问题
import asyncio
# 设置合适的事件循环策略
if sys.platform == "win32":
# Windows系统使用ProactorEventLoop
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
else:
# Linux/Mac系统使用默认策略
asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy())
self.is_async()
self.driver = sync_playwright().start()
# 根据浏览器名称选择浏览器类型
View File
+1
View File
@@ -21,6 +21,7 @@ def set_token(data:any,ext_data:any=None):
return
wx_cfg.set("token", data.get("token", ""))
wx_cfg.set("cookie", data.get("cookies_str", ""))
wx_cfg.set("fingerprint", data.get("fingerprint", ""))
wx_cfg.set("expiry", data.get("expiry", {}))
print_success(f"Token:{data.get('token')} \n到期时间:{data.get('expiry')['expiry_time']}\n")
if ext_data is not None:
+103 -27
View File
@@ -81,7 +81,82 @@ class Wx:
except Exception as e:
print(f"提取token时出错: {str(e)}")
return None
def switch_account(self, username: str = ""):
"""切换账号功能
Args:
username: 目标账号的用户名,如果为空则切换到其他可用账号
"""
print("开始切换账号...")
try:
self.Token(isClose=False)
if self._haslogin is False:
self.GetCode(Success)
time.sleep(60)
return False
time.sleep(1)
if not hasattr(self, 'controller') or not self.controller.page:
print_error("浏览器未启动,无法切换账号")
return False
page = self.controller.page
# 等待页面加载完成
page.wait_for_load_state("networkidle")
# 点击账号信息区域打开账号面板
account_info = page.locator(".weui-desktop-account__info")
if account_info.count() > 0:
account_info.click()
time.sleep(1)
# 等待账号面板显示
account_panel = page.locator(".account_box-panel")
if account_panel.count() > 0:
# 查找切换账号按钮(更精确的选择器)
switch_account_link = account_panel.locator("li.account_box-panel-item:has-text('切换账号') a")
if switch_account_link.count() > 0:
print_info("找到切换账号按钮,点击切换...")
switch_account_link.click()
time.sleep(3)
try:
# 查找可切换的账号(排除当前登录账号)
accounts = page.locator(
".switch-account-dialog .switch-account-dialog_section:has-text('公众号') .section-item:not(:has-text('当前登录')),"
".switch-account-dialog .switch-account-dialog_section:has-text('服务号') .section-item:not(:has-text('当前登录'))"
)
account_count = accounts.count()
print(f"当前一共有{account_count}个可切换账号")
import random
if account_count > 0:
# 点击第一个可切换的账号
time.sleep(3)
random_index = random.randint(0, account_count - 1)
p=accounts.nth(random_index).locator("p")
account_name=p.text_content()
print(f"切换账号: {account_name}")
p.click()
# 等待页面加载并验证切换成功
page.wait_for_load_state("networkidle", timeout=10000)
print_success("账号切换成功")
self.Call_Success()
return True
else:
print_warning("没有找到可切换的账号")
except Exception as e:
print_error(f"切换账号时发生错误: {str(e)}")
return False
else:
print_warning("未找到切换账号按钮")
else:
print_warning("账号面板未打开")
else:
print_warning("未找到账号信息区域")
except Exception as e:
print_error(f"切换账号时发生错误: {str(e)}")
return False
def GetCode(self,CallBack=None,Notice=None):
self.Notice=Notice
if self.check_lock():
@@ -116,13 +191,14 @@ class Wx:
except Exception as e:
raise Exception(f"浏览器关闭") # 重新抛出异常以便外部捕获处理
def HasLogin(self):
return self.HasLogin
with self._login_lock:
return self._haslogin
def schedule_refresh(self):
if self.refresh_interval <= 0:
return
with self._login_lock:
if not self.HasLogin or not hasattr(self, 'controller') or self.controller is None:
if not self._haslogin or not hasattr(self, 'controller') or self.controller is None:
return
try:
@@ -134,9 +210,9 @@ class Wx:
except Exception as e:
print_error(f"定时刷新任务失败: {str(e)}")
# 不再抛出异常,避免无限循环
def Token(self, CallBack=None):
def Token(self, callback=None,isClose=True):
try:
self.CallBack = CallBack
self.CallBack = callback
if not getStatus():
print_warning("登录状态检查失败")
return None
@@ -175,7 +251,10 @@ class Wx:
qrcode.wait_for(state="visible", timeout=self.wait_time * 1000)
qrcode.click()
time.sleep(2)
hasLogin=page.locator("body:has-text('使用账号登录')")
if hasLogin.count()>0:
self._haslogin=False
return False
return self.Call_Success()
except ImportError as e:
print_error(f"导入模块失败: {str(e)}")
@@ -185,7 +264,8 @@ class Wx:
return None
finally:
# 不在这里清理,让Call_Success处理清理
self.controller.cleanup()
if isClose:
self.controller.cleanup()
pass
def isLock(self):
if self.isLock:
@@ -216,7 +296,7 @@ class Wx:
self.set_lock()
with self._login_lock:
self.HasLogin = False
self._haslogin = False
# 清理现有资源
self.cleanup_resources()
@@ -271,7 +351,7 @@ class Wx:
from .success import setStatus
with self._login_lock:
self.HasLogin=True
self._haslogin=True
setStatus(True)
self.CallBack=CallBack
self.Call_Success()
@@ -285,17 +365,17 @@ class Wx:
finally:
self.release_lock()
# 只有在NeedExit为True且未登录成功时才清理资源
if NeedExit and 'controller' in locals() and not self.HasLogin:
if NeedExit and 'controller' in locals() and not self._haslogin:
self.controller.cleanup()
self.Clean()
return self.SESSION
def format_token(self,cookies:any,token=""):
def format_token(self, cookies: list, token: str = ""):
cookies_str=""
for cookie in cookies:
# print(f"{cookie['name']}={cookie['value']}")
cookies_str+=f"{cookie['name']}={cookie['value']}; "
if 'token' in cookie['name'].lower():
token= cookie['value']
token= token or cookie['value']
# 计算 slave_sid cookie 有效时间
cookie_expiry = expire(cookies)
return{
@@ -319,9 +399,9 @@ class Wx:
# print("\n获取到的Cookie:")
self.SESSION=self.format_token(cookies,str(token))
with self._login_lock:
self.HasLogin=False if self.SESSION["expiry"] is None else True
self._haslogin=False if self.SESSION["expiry"] is None else True
# 登录成功后不立即清理二维码,保持浏览器运行
if self.HasLogin:
if self._haslogin:
try:
# 使用更健壮的选择器定位元素
self.ext_data = self._extract_wechat_data()
@@ -352,13 +432,13 @@ class Wx:
# 使用更健壮的选择器,增加备选方案
selectors = {
"wx_app_name": [".account-name", ".nickname", ".account_nickname"],
"wx_logo": [".account-avatar img", ".avatar img"],
"wx_read_yesterday": [".data-item:nth-child(1) .number", ".data-item:first-child .number", "[data-label='阅读'] .number"],
"wx_share_yesterday": [".data-item:nth-child(2) .number", ".data-item:nth-child(1) + .data-item .number", "[data-label='分享'] .number"],
"wx_watch_yesterday": [".data-item:nth-child(3) .number", ".data-item:last-child .number", "[data-label='在看'] .number", ".data-item .number"],
"wx_yuan_count": [".original-count .number", "[data-label='原创'] .number"],
"wx_user_count": [".user-count .number", "[data-label='关注'] .number"]
"wx_app_name": [".weui-desktop_name", ".acount_box-nickname", ".account_box-panel-head__nickname"],
"wx_logo": [".weui-desktop-account__img", ".weui-desktop-account__thumb", ".account_box-panel-head__thumb"],
"wx_read_yesterday": [".weui-desktop-data-overview:nth-child(1) .weui-desktop-data-overview__desc span", ".weui-desktop-data-overview:first-child .weui-desktop-data-overview__desc span"],
"wx_share_yesterday": [".weui-desktop-data-overview:nth-child(2) .weui-desktop-data-overview__desc span", ".weui-desktop-data-overview:nth-child(1) + .weui-desktop-data-overview .weui-desktop-data-overview__desc span"],
"wx_watch_yesterday": [".weui-desktop-data-overview:nth-child(3) .weui-desktop-data-overview__desc span", ".weui-desktop-data-overview:last-child .weui-desktop-data-overview__desc span"],
"wx_yuan_count": [".original_cnt .weui-desktop-user_sum span", ".weui-desktop-user_sum.original_cnt span"],
"wx_user_count": [".weui-desktop-user_sum:not(.original_cnt) span", ".weui-desktop-user_num .weui-desktop-user_sum span"]
}
for key, selector_list in selectors.items():
@@ -410,7 +490,7 @@ class Wx:
# 重置状态
with self._login_lock:
self.HasLogin = False
self._haslogin = False
self.HasCode = False
print_info("资源清理完成")
@@ -425,8 +505,7 @@ class Wx:
self.controller.cleanup()
rel=True
except Exception as e:
print("浏览器未启动")
# print(e)
print_warning("浏览器未启动或已关闭")
pass
return rel
def Clean(self):
@@ -470,9 +549,6 @@ class Wx:
except:
return False
def DoSuccess(cookies:any) -> dict:
data=WX_API.format_token(cookies)
Success(data)
WX_API = Wx()
def GetCode(CallBack:any=None,NeedExit=True):
+73 -9
View File
@@ -6,9 +6,11 @@
from ast import Call
import os
# import traceback
import re
import time
import json
import base64
from urllib import response
from attr import s
import requests
from typing import Optional, Dict, Any, Callable
@@ -41,7 +43,7 @@ class WeChatAPI:
self.session = requests.Session()
self.token = None
self.cookies_dict=[]
self.cookies = {}
self.cookies:Optional[Dict[str,str]] = {}
self.qr_code_path = "static/wx_qrcode.png"
self.wx_login_url=f"{self.qr_code_path}"
# 线程安全
@@ -414,7 +416,7 @@ class WeChatAPI:
if not os.path.exists(self.qr_code_path):
return "not_exists"
check_url=f"{self.base_url}/cgi-bin/scanloginqrcode"
self.fingerprint=self.cookies.get("fingerprint")
self.fingerprint=self.cookies.get("fingerprint") or self._generate_uuid()
params = {
"action": "ask",
"fingerprint": self.fingerprint,
@@ -464,11 +466,12 @@ class WeChatAPI:
self._clean_qr_code()
from driver.cookies import expire
# 调用成功回调
self._get_account_info()
logger.info("登录成功!")
if self._get_account_info() is not None:
logger.info("登录成功!")
return True
except Exception as e:
logger.error(f"处理登录失败: {str(e)}")
return False
def _extract_login_info(self):
"""
提取登录信息(token和cookies
@@ -621,6 +624,7 @@ class WeChatAPI:
'cookies': self.cookies,
'cookies_str': self._format_cookies_string(),
'token': self.token,
'fingerprint': self.fingerprint,
'wx_login_url': self.qr_code_path,
'expiry': expire(self.cookies_dict)
}
@@ -633,6 +637,67 @@ class WeChatAPI:
logger.error(f"获取账号信息失败: {str(e)}")
return None
def switch_account(self,username:str=""):
"""切换微信公众号账号"""
self.login_with_token()
url = f"{self.base_url}/cgi-bin/switchacct?action=switch"
headers = {
"accept": "*/*",
"accept-language": "zh-CN,zh;q=0.9",
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
"priority": "u=1, i",
"sec-ch-ua": "\"Not?A_Brand\";v=\"99\", \"Chromium\";v=\"130\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"x-requested-with": "XMLHttpRequest",
}
headers["Referer"]=f"{self.base_url}/cgi-bin/home?t=home/index&lang=zh_CN&token={self.token}"
params = {
"f": "json",
"username": username,
"fingerprint": self.fingerprint,
"token": self.token,
"lang": "zh_CN",
"ajax": "1"
}
try:
response = self.session.post(
url,
headers=headers,
data=params,
cookies={},
allow_redirects=True
)
response.raise_for_status() # 检查HTTP错误
# 解析JSON响应
data = response.json()
print("切换账号响应:", data)
if data.get("base_resp").get("ret") == 0:
self._redirect()
return data
except requests.exceptions.RequestException as e:
print(f"请求出错: {e}")
return None
except json.JSONDecodeError as e:
print(f"JSON解析出错: {e}")
return None
def _redirect(self):
url=f"https://mp.weixin.qq.com/cgi-bin/loginpage?url=/cgi-bin/home?t=home/index&lang=zh_CN&token={self.token}"
response=self.session.get(url)
response.raise_for_status()
self.cookies = requests.utils.dict_from_cookiejar(response.cookies) if response.cookies else {}
self.session.cookies.update(self.cookies)
self.token=self.cookies.get("token")
self._handle_login_success()
def _get_account_list(self) -> Optional[Dict[str, Any]]:
"""
获取账号列表
@@ -650,7 +715,7 @@ class WeChatAPI:
# 构建请求URL
url = f"{self.base_url}/cgi-bin/switchacct"
self.fingerprint=self.cookies.get("fingerprint")
self.fingerprint=self.cookies.get("fingerprint") or self._generate_uuid()
# 设置请求参数
params = {
'action': 'get_acct_list',
@@ -704,7 +769,7 @@ class WeChatAPI:
except Exception as e:
logger.error(f"清理二维码文件失败: {str(e)}")
def login_with_token(self, token: str="", cookies: Optional[Dict[str, str]] = None) -> bool:
def login_with_token(self, token: str="", cookies:Any = None) -> bool:
"""
使用token登录
@@ -737,8 +802,7 @@ class WeChatAPI:
if 'home' in response.url:
self.is_logged_in = True
logger.info("Token登录成功")
self._handle_login_success()
return True
return self._handle_login_success()
else:
logger.warning("Token登录失败")
return False
+8 -11
View File
@@ -59,21 +59,19 @@ def testMd2Doc():
def testToken():
from driver.auth import auth
auth()
# input("按任意键退出")
def testCheckAuth():
from driver.auth import auth
input("按任意键退出")
def testLogin():
from driver.base import WX_API
from driver.success import Success
rel=WX_API.Token(Success)
if rel==False:
de_url=WX_API.GetCode(Success)
print(de_url)
input("按任意键退出")
# de_url=WX_API.GetCode(Success)
WX_API.switch_account("gh_804eaba2350a")
# rel=WX_API.Token(Success)
# if rel==False:
# de_url=WX_API.GetCode(Success)
# print(de_url)
# input("按任意键退出")
def testNotice():
from jobs.notice import sys_notice
text="""
@@ -157,8 +155,7 @@ if __name__=="__main__":
# testWeb()
# testNotice()
# testMd2Doc()
# testToken()
# testCheckAuth()
testLogin()
# testCheckAuth()
# testToken() # 注释掉避免线程冲突
# testMarkDown()