refactor(maizone): 优化代码格式并移除多余日志
对 `qzone_service` 和 `monitor_service` 进行了代码风格调整和重构。 - 在 `qzone_service.py` 中,统一了函数定义、字典和参数列表的格式,使其更符合代码规范,提高了可读性。 - 在 `monitor_service.py` 中,移除了监控任务开始时的一条多余日志记录,以减少不必要的日志输出。 - 在 `qzone_service.py` 的 `_monitor_list_feeds` 方法中,增加了对API返回格式的校验,以增强代码的健壮性。
This commit is contained in:
committed by
Windpicker-owo
parent
0144321254
commit
91c0e31548
@@ -55,7 +55,6 @@ class MonitorService:
|
|||||||
|
|
||||||
interval_minutes = self.get_config("monitor.interval_minutes", 10)
|
interval_minutes = self.get_config("monitor.interval_minutes", 10)
|
||||||
|
|
||||||
logger.info("开始执行好友动态监控...")
|
|
||||||
await self.qzone_service.monitor_feeds()
|
await self.qzone_service.monitor_feeds()
|
||||||
|
|
||||||
logger.info(f"本轮监控完成,将在 {interval_minutes} 分钟后进行下一次检查。")
|
logger.info(f"本轮监控完成,将在 {interval_minutes} 分钟后进行下一次检查。")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
QQ空间服务模块
|
QQ空间服务模块
|
||||||
封装了所有与QQ空间API的直接交互,是插件的核心业务逻辑层。
|
封装了所有与QQ空间API的直接交互,是插件的核心业务逻辑层。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
@@ -29,6 +30,7 @@ class QZoneService:
|
|||||||
"""
|
"""
|
||||||
QQ空间服务类,负责所有API交互和业务流程编排。
|
QQ空间服务类,负责所有API交互和业务流程编排。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# --- API Endpoints ---
|
# --- API Endpoints ---
|
||||||
ZONE_LIST_URL = "https://user.qzone.qq.com/proxy/domain/ic2.qzone.qq.com/cgi-bin/feeds/feeds3_html_more"
|
ZONE_LIST_URL = "https://user.qzone.qq.com/proxy/domain/ic2.qzone.qq.com/cgi-bin/feeds/feeds3_html_more"
|
||||||
EMOTION_PUBLISH_URL = "https://user.qzone.qq.com/proxy/domain/taotao.qzone.qq.com/cgi-bin/emotion_cgi_publish_v6"
|
EMOTION_PUBLISH_URL = "https://user.qzone.qq.com/proxy/domain/taotao.qzone.qq.com/cgi-bin/emotion_cgi_publish_v6"
|
||||||
@@ -37,8 +39,13 @@ class QZoneService:
|
|||||||
LIST_URL = "https://user.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6"
|
LIST_URL = "https://user.qzone.qq.com/proxy/domain/taotao.qq.com/cgi-bin/emotion_cgi_msglist_v6"
|
||||||
REPLY_URL = "https://user.qzone.qq.com/proxy/domain/taotao.qzone.qq.com/cgi-bin/emotion_cgi_re_feeds"
|
REPLY_URL = "https://user.qzone.qq.com/proxy/domain/taotao.qzone.qq.com/cgi-bin/emotion_cgi_re_feeds"
|
||||||
|
|
||||||
|
def __init__(
|
||||||
def __init__(self, get_config: Callable, content_service: ContentService, image_service: ImageService, cookie_service: CookieService):
|
self,
|
||||||
|
get_config: Callable,
|
||||||
|
content_service: ContentService,
|
||||||
|
image_service: ImageService,
|
||||||
|
cookie_service: CookieService,
|
||||||
|
):
|
||||||
self.get_config = get_config
|
self.get_config = get_config
|
||||||
self.content_service = content_service
|
self.content_service = content_service
|
||||||
self.image_service = image_service
|
self.image_service = image_service
|
||||||
@@ -171,24 +178,31 @@ class QZoneService:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# 筛选出未被自己回复过的主评论
|
# 筛选出未被自己回复过的主评论
|
||||||
my_comment_tids = {c['parent_tid'] for c in comments if c.get('parent_tid') and c.get('qq_account') == qq_account}
|
my_comment_tids = {
|
||||||
comments_to_reply = [c for c in comments if not c.get('parent_tid') and c.get('comment_tid') not in my_comment_tids]
|
c["parent_tid"] for c in comments if c.get("parent_tid") and c.get("qq_account") == qq_account
|
||||||
|
}
|
||||||
|
comments_to_reply = [
|
||||||
|
c for c in comments if not c.get("parent_tid") and c.get("comment_tid") not in my_comment_tids
|
||||||
|
]
|
||||||
|
|
||||||
if not comments_to_reply:
|
if not comments_to_reply:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info(f"发现自己说说下的 {len(comments_to_reply)} 条新评论,准备回复...")
|
logger.info(f"发现自己说说下的 {len(comments_to_reply)} 条新评论,准备回复...")
|
||||||
for comment in comments_to_reply:
|
for comment in comments_to_reply:
|
||||||
reply_content = await self.content_service.generate_comment_reply(content, comment.get('content', ''), comment.get('nickname', ''))
|
reply_content = await self.content_service.generate_comment_reply(
|
||||||
|
content, comment.get("content", ""), comment.get("nickname", "")
|
||||||
|
)
|
||||||
if reply_content:
|
if reply_content:
|
||||||
success = await api_client["reply"](fid, qq_account, comment.get('nickname', ''), reply_content, comment.get('comment_tid'))
|
success = await api_client["reply"](
|
||||||
|
fid, qq_account, comment.get("nickname", ""), reply_content, comment.get("comment_tid")
|
||||||
|
)
|
||||||
if success:
|
if success:
|
||||||
logger.info(f"成功回复'{comment.get('nickname', '')}'的评论: '{reply_content}'")
|
logger.info(f"成功回复'{comment.get('nickname', '')}'的评论: '{reply_content}'")
|
||||||
else:
|
else:
|
||||||
logger.error(f"回复'{comment.get('nickname', '')}'的评论失败")
|
logger.error(f"回复'{comment.get('nickname', '')}'的评论失败")
|
||||||
await asyncio.sleep(random.uniform(10, 20))
|
await asyncio.sleep(random.uniform(10, 20))
|
||||||
|
|
||||||
|
|
||||||
async def _process_single_feed(self, feed: Dict, api_client: Dict, target_qq: str, target_name: str):
|
async def _process_single_feed(self, feed: Dict, api_client: Dict, target_qq: str, target_name: str):
|
||||||
"""处理单条说说,决定是否评论和点赞"""
|
"""处理单条说说,决定是否评论和点赞"""
|
||||||
content = feed.get("content", "")
|
content = feed.get("content", "")
|
||||||
@@ -228,21 +242,26 @@ class QZoneService:
|
|||||||
|
|
||||||
async def _get_api_client(self, qq_account: str, stream_id: Optional[str]) -> Optional[Dict]:
|
async def _get_api_client(self, qq_account: str, stream_id: Optional[str]) -> Optional[Dict]:
|
||||||
cookies = await self.cookie_service.get_cookies(qq_account, stream_id)
|
cookies = await self.cookie_service.get_cookies(qq_account, stream_id)
|
||||||
if not cookies: return None
|
if not cookies:
|
||||||
|
return None
|
||||||
|
|
||||||
p_skey = cookies.get('p_skey') or cookies.get('p_skey'.upper())
|
p_skey = cookies.get("p_skey") or cookies.get("p_skey".upper())
|
||||||
if not p_skey: return None
|
if not p_skey:
|
||||||
|
return None
|
||||||
|
|
||||||
gtk = self._generate_gtk(p_skey)
|
gtk = self._generate_gtk(p_skey)
|
||||||
uin = cookies.get('uin', '').lstrip('o')
|
uin = cookies.get("uin", "").lstrip("o")
|
||||||
|
|
||||||
async def _request(method, url, params=None, data=None, headers=None):
|
async def _request(method, url, params=None, data=None, headers=None):
|
||||||
final_headers = {'referer': f'https://user.qzone.qq.com/{uin}', 'origin': 'https://user.qzone.qq.com'}
|
final_headers = {"referer": f"https://user.qzone.qq.com/{uin}", "origin": "https://user.qzone.qq.com"}
|
||||||
if headers: final_headers.update(headers)
|
if headers:
|
||||||
|
final_headers.update(headers)
|
||||||
|
|
||||||
async with aiohttp.ClientSession(cookies=cookies) as session:
|
async with aiohttp.ClientSession(cookies=cookies) as session:
|
||||||
timeout = aiohttp.ClientTimeout(total=20)
|
timeout = aiohttp.ClientTimeout(total=20)
|
||||||
async with session.request(method, url, params=params, data=data, headers=final_headers, timeout=timeout) as response:
|
async with session.request(
|
||||||
|
method, url, params=params, data=data, headers=final_headers, timeout=timeout
|
||||||
|
) as response:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return await response.text()
|
return await response.text()
|
||||||
|
|
||||||
@@ -250,11 +269,18 @@ class QZoneService:
|
|||||||
"""发布说说"""
|
"""发布说说"""
|
||||||
try:
|
try:
|
||||||
post_data = {
|
post_data = {
|
||||||
"syn_tweet_verson": "1", "paramstr": "1", "who": "1",
|
"syn_tweet_verson": "1",
|
||||||
"con": content, "feedversion": "1", "ver": "1",
|
"paramstr": "1",
|
||||||
"ugc_right": "1", "to_sign": "0", "hostuin": uin,
|
"who": "1",
|
||||||
"code_version": "1", "format": "json",
|
"con": content,
|
||||||
"qzreferrer": f"https://user.qzone.qq.com/{uin}"
|
"feedversion": "1",
|
||||||
|
"ver": "1",
|
||||||
|
"ugc_right": "1",
|
||||||
|
"to_sign": "0",
|
||||||
|
"hostuin": uin,
|
||||||
|
"code_version": "1",
|
||||||
|
"format": "json",
|
||||||
|
"qzreferrer": f"https://user.qzone.qq.com/{uin}",
|
||||||
}
|
}
|
||||||
if images:
|
if images:
|
||||||
pic_bos, richvals = [], []
|
pic_bos, richvals = [], []
|
||||||
@@ -269,13 +295,13 @@ class QZoneService:
|
|||||||
|
|
||||||
# Dummy data for illustration
|
# Dummy data for illustration
|
||||||
if images:
|
if images:
|
||||||
post_data['pic_bo'] = 'dummy_pic_bo'
|
post_data["pic_bo"] = "dummy_pic_bo"
|
||||||
post_data['richtype'] = '1'
|
post_data["richtype"] = "1"
|
||||||
post_data['richval'] = 'dummy_rich_val'
|
post_data["richval"] = "dummy_rich_val"
|
||||||
|
|
||||||
res_text = await _request("POST", self.EMOTION_PUBLISH_URL, params={'g_tk': gtk}, data=post_data)
|
res_text = await _request("POST", self.EMOTION_PUBLISH_URL, params={"g_tk": gtk}, data=post_data)
|
||||||
result = json.loads(res_text)
|
result = json.loads(res_text)
|
||||||
tid = result.get('tid', '')
|
tid = result.get("tid", "")
|
||||||
return bool(tid), tid
|
return bool(tid), tid
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"发布说说异常: {e}", exc_info=True)
|
logger.error(f"发布说说异常: {e}", exc_info=True)
|
||||||
@@ -285,27 +311,44 @@ class QZoneService:
|
|||||||
"""获取指定用户说说列表"""
|
"""获取指定用户说说列表"""
|
||||||
try:
|
try:
|
||||||
params = {
|
params = {
|
||||||
'g_tk': gtk, "uin": t_qq, "ftype": 0, "sort": 0, "pos": 0,
|
"g_tk": gtk,
|
||||||
"num": num, "replynum": 100, "callback": "_preloadCallback",
|
"uin": t_qq,
|
||||||
"code_version": 1, "format": "jsonp", "need_comment": 1
|
"ftype": 0,
|
||||||
|
"sort": 0,
|
||||||
|
"pos": 0,
|
||||||
|
"num": num,
|
||||||
|
"replynum": 100,
|
||||||
|
"callback": "_preloadCallback",
|
||||||
|
"code_version": 1,
|
||||||
|
"format": "jsonp",
|
||||||
|
"need_comment": 1,
|
||||||
}
|
}
|
||||||
res_text = await _request("GET", self.LIST_URL, params=params)
|
res_text = await _request("GET", self.LIST_URL, params=params)
|
||||||
json_str = res_text[len('_preloadCallback('):-2]
|
json_str = res_text[len("_preloadCallback(") : -2]
|
||||||
json_data = json.loads(json_str)
|
json_data = json.loads(json_str)
|
||||||
|
|
||||||
if json_data.get('code') != 0: return []
|
if json_data.get("code") != 0:
|
||||||
|
return []
|
||||||
|
|
||||||
feeds_list = []
|
feeds_list = []
|
||||||
my_name = json_data.get('logininfo', {}).get('name', '')
|
my_name = json_data.get("logininfo", {}).get("name", "")
|
||||||
for msg in json_data.get("msglist", []):
|
for msg in json_data.get("msglist", []):
|
||||||
is_commented = any(c.get("name") == my_name for c in msg.get("commentlist", []) if isinstance(c, dict))
|
is_commented = any(
|
||||||
|
c.get("name") == my_name for c in msg.get("commentlist", []) if isinstance(c, dict)
|
||||||
|
)
|
||||||
if not is_commented:
|
if not is_commented:
|
||||||
feeds_list.append({
|
feeds_list.append(
|
||||||
|
{
|
||||||
"tid": msg.get("tid", ""),
|
"tid": msg.get("tid", ""),
|
||||||
"content": msg.get("content", ""),
|
"content": msg.get("content", ""),
|
||||||
"created_time": time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(msg.get("created_time", 0))),
|
"created_time": time.strftime(
|
||||||
"rt_con": msg.get("rt_con", {}).get("content", "") if isinstance(msg.get("rt_con"), dict) else ""
|
"%Y-%m-%d %H:%M:%S", time.localtime(msg.get("created_time", 0))
|
||||||
})
|
),
|
||||||
|
"rt_con": msg.get("rt_con", {}).get("content", "")
|
||||||
|
if isinstance(msg.get("rt_con"), dict)
|
||||||
|
else "",
|
||||||
|
}
|
||||||
|
)
|
||||||
return feeds_list
|
return feeds_list
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"获取说说列表失败: {e}", exc_info=True)
|
logger.error(f"获取说说列表失败: {e}", exc_info=True)
|
||||||
@@ -315,9 +358,15 @@ class QZoneService:
|
|||||||
"""评论说说"""
|
"""评论说说"""
|
||||||
try:
|
try:
|
||||||
data = {
|
data = {
|
||||||
"topicId": f'{t_qq}_{feed_id}__1', "uin": uin, "hostUin": t_qq,
|
"topicId": f"{t_qq}_{feed_id}__1",
|
||||||
"content": text, "format": "fs", "plat": "qzone", "source": "ic",
|
"uin": uin,
|
||||||
"platformid": 52, "ref": "feeds"
|
"hostUin": t_qq,
|
||||||
|
"content": text,
|
||||||
|
"format": "fs",
|
||||||
|
"plat": "qzone",
|
||||||
|
"source": "ic",
|
||||||
|
"platformid": 52,
|
||||||
|
"ref": "feeds",
|
||||||
}
|
}
|
||||||
await _request("POST", self.COMMENT_URL, params={"g_tk": gtk}, data=data)
|
await _request("POST", self.COMMENT_URL, params={"g_tk": gtk}, data=data)
|
||||||
return True
|
return True
|
||||||
@@ -329,12 +378,19 @@ class QZoneService:
|
|||||||
"""点赞说说"""
|
"""点赞说说"""
|
||||||
try:
|
try:
|
||||||
data = {
|
data = {
|
||||||
'opuin': uin, 'unikey': f'http://user.qzone.qq.com/{t_qq}/mood/{feed_id}',
|
"opuin": uin,
|
||||||
'curkey': f'http://user.qzone.qq.com/{t_qq}/mood/{feed_id}',
|
"unikey": f"http://user.qzone.qq.com/{t_qq}/mood/{feed_id}",
|
||||||
'from': 1, 'appid': 311, 'typeid': 0, 'abstime': int(time.time()),
|
"curkey": f"http://user.qzone.qq.com/{t_qq}/mood/{feed_id}",
|
||||||
'fid': feed_id, 'active': 0, 'format': 'json', 'fupdate': 1
|
"from": 1,
|
||||||
|
"appid": 311,
|
||||||
|
"typeid": 0,
|
||||||
|
"abstime": int(time.time()),
|
||||||
|
"fid": feed_id,
|
||||||
|
"active": 0,
|
||||||
|
"format": "json",
|
||||||
|
"fupdate": 1,
|
||||||
}
|
}
|
||||||
await _request("POST", self.DOLIKE_URL, params={'g_tk': gtk}, data=data)
|
await _request("POST", self.DOLIKE_URL, params={"g_tk": gtk}, data=data)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"点赞说说异常: {e}", exc_info=True)
|
logger.error(f"点赞说说异常: {e}", exc_info=True)
|
||||||
@@ -355,7 +411,7 @@ class QZoneService:
|
|||||||
"ref": "feeds",
|
"ref": "feeds",
|
||||||
"richtype": "",
|
"richtype": "",
|
||||||
"richval": "",
|
"richval": "",
|
||||||
"paramstr": f"@{target_name} {content}"
|
"paramstr": f"@{target_name} {content}",
|
||||||
}
|
}
|
||||||
await _request("POST", self.REPLY_URL, params={"g_tk": gtk}, data=data)
|
await _request("POST", self.REPLY_URL, params={"g_tk": gtk}, data=data)
|
||||||
return True
|
return True
|
||||||
@@ -367,38 +423,55 @@ class QZoneService:
|
|||||||
"""监控好友动态"""
|
"""监控好友动态"""
|
||||||
try:
|
try:
|
||||||
params = {
|
params = {
|
||||||
"uin": uin, "scope": 0, "view": 1, "filter": "all", "flag": 1,
|
"uin": uin,
|
||||||
"applist": "all", "pagenum": 1, "count": num, "format": "json",
|
"scope": 0,
|
||||||
"g_tk": gtk, "useutf8": 1, "outputhtmlfeed": 1
|
"view": 1,
|
||||||
|
"filter": "all",
|
||||||
|
"flag": 1,
|
||||||
|
"applist": "all",
|
||||||
|
"pagenum": 1,
|
||||||
|
"count": num,
|
||||||
|
"format": "json",
|
||||||
|
"g_tk": gtk,
|
||||||
|
"useutf8": 1,
|
||||||
|
"outputhtmlfeed": 1,
|
||||||
}
|
}
|
||||||
res_text = await _request("GET", self.ZONE_LIST_URL, params=params)
|
res_text = await _request("GET", self.ZONE_LIST_URL, params=params)
|
||||||
json_str = res_text[len('_Callback('):-2].replace('undefined', 'null')
|
|
||||||
|
# 增加对返回内容的校验
|
||||||
|
if not res_text.startswith("_Callback(") or not res_text.endswith(");"):
|
||||||
|
logger.warning(f"监控好友动态返回格式异常: {res_text}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
json_str = res_text[len("_Callback(") : -2].replace("undefined", "null")
|
||||||
json_data = json5.loads(json_str)
|
json_data = json5.loads(json_str)
|
||||||
feeds_data = []
|
feeds_data = []
|
||||||
if isinstance(json_data, dict):
|
if isinstance(json_data, dict):
|
||||||
data_level1 = json_data.get('data')
|
data_level1 = json_data.get("data")
|
||||||
if isinstance(data_level1, dict):
|
if isinstance(data_level1, dict):
|
||||||
feeds_data = data_level1.get('data', [])
|
feeds_data = data_level1.get("data", [])
|
||||||
|
|
||||||
feeds_list = []
|
feeds_list = []
|
||||||
for feed in feeds_data:
|
for feed in feeds_data:
|
||||||
if str(feed.get('appid', '')) != '311' or str(feed.get('uin', '')) == str(uin):
|
if str(feed.get("appid", "")) != "311" or str(feed.get("uin", "")) == str(uin):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
html_content = feed.get('html', '')
|
html_content = feed.get("html", "")
|
||||||
soup = bs4.BeautifulSoup(html_content, 'html.parser')
|
soup = bs4.BeautifulSoup(html_content, "html.parser")
|
||||||
like_btn = soup.find('a', class_='qz_like_btn_v3')
|
like_btn = soup.find("a", class_="qz_like_btn_v3")
|
||||||
if isinstance(like_btn, bs4.element.Tag) and like_btn.get('data-islike') == '1':
|
if isinstance(like_btn, bs4.element.Tag) and like_btn.get("data-islike") == "1":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
text_div = soup.find('div', class_='f-info')
|
text_div = soup.find("div", class_="f-info")
|
||||||
text = text_div.get_text(strip=True) if text_div else ""
|
text = text_div.get_text(strip=True) if text_div else ""
|
||||||
|
|
||||||
feeds_list.append({
|
feeds_list.append(
|
||||||
'target_qq': feed.get('uin'),
|
{
|
||||||
'tid': feed.get('key'),
|
"target_qq": feed.get("uin"),
|
||||||
'content': text,
|
"tid": feed.get("key"),
|
||||||
})
|
"content": text,
|
||||||
|
}
|
||||||
|
)
|
||||||
return feeds_list
|
return feeds_list
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"监控好友动态失败: {e}", exc_info=True)
|
logger.error(f"监控好友动态失败: {e}", exc_info=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user