mirror of
https://github.com/rachelos/we-mp-rss.git
synced 2026-09-17 17:18:52 +08:00
1.4.8-Fix
This commit is contained in:
+2
-1
@@ -17,4 +17,5 @@ build
|
||||
dist
|
||||
data1
|
||||
doc2pdf/*.docx
|
||||
doc2pdf/*.pdf
|
||||
doc2pdf/*.pdf
|
||||
demo.txt
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
# 根据浏览器名称选择浏览器类型
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user