mirror of
https://github.com/smallfawn/QLScriptPublic.git
synced 2026-11-06 06:46:48 +08:00
394 lines
14 KiB
Python
394 lines
14 KiB
Python
#by:哆啦A梦
|
|
#入口:http://mx.qrurl.net/h5/wxa/link?sid=26407uif5Oq
|
|
#BREO变量填写wx_server里的openid/账号标识,多账号换行分割
|
|
#需要配置wx_server_url、wx_auth,用于获取wx.login code
|
|
#账号变量名:BREO
|
|
#new Env("BREO")
|
|
#cron 8 9,10,11 * * *
|
|
|
|
|
|
import requests
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
if hasattr(sys.stdout, "reconfigure"):
|
|
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
if hasattr(sys.stderr, "reconfigure"):
|
|
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
|
|
MINI_APP_ID = "wx61457400e4212cec"
|
|
WX_SERVER_URL = os.getenv("wx_server_url", "http://192.168.31.196:8787").rstrip("/")
|
|
WX_AUTH = os.getenv("wx_auth", "")
|
|
TOKEN_CACHE_PATH = Path(__file__).with_name("BREO_token_cache.json")
|
|
LOGIN_BASE = "https://breoplus.breo.cn/app/minic"
|
|
APP_BASE = "https://breoplus.breo.cn/breo-app"
|
|
DEFAULT_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 MicroMessenger/7.0.20.1781(0x6700143B) NetType/WIFI MiniProgramEnv/Windows WindowsWechat/WMPF"
|
|
|
|
session = requests.Session()
|
|
|
|
def read_token_cache():
|
|
try:
|
|
if not TOKEN_CACHE_PATH.exists():
|
|
return {}
|
|
return json.loads(TOKEN_CACHE_PATH.read_text(encoding="utf-8")) or {}
|
|
except Exception:
|
|
return {}
|
|
|
|
def write_token_cache(cache):
|
|
try:
|
|
TOKEN_CACHE_PATH.write_text(json.dumps(cache, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
except Exception as e:
|
|
print(f"⚠️ 写入token缓存失败: {e}")
|
|
|
|
def mask_token(token):
|
|
if not token:
|
|
return ""
|
|
return f"{token[:6]}***{token[-6:]}"
|
|
|
|
def is_success(result):
|
|
return result.get("success") is True or str(result.get("code")) in ("0000", "200", "200.0")
|
|
|
|
def is_token_error(message):
|
|
text = str(message)
|
|
return any(key in text for key in ["40101", "40102", "token", "登录", "授权", "过期", "失效"])
|
|
|
|
def wx_headers():
|
|
return {
|
|
"auth": WX_AUTH,
|
|
"User-Agent": DEFAULT_UA,
|
|
"Content-Type": "application/json",
|
|
}
|
|
|
|
def app_headers(token=None):
|
|
headers = {
|
|
"User-Agent": DEFAULT_UA,
|
|
"content-Type": "application/json",
|
|
"Content-Type": "application/json",
|
|
"deviceInfo": "{}",
|
|
"Referer": "https://servicewechat.com/wx61457400e4212cec/390/page-frame.html",
|
|
}
|
|
if token:
|
|
headers["token"] = token
|
|
return headers
|
|
|
|
def breo_task_headers(token):
|
|
return {
|
|
"token": token,
|
|
"device-type": "Xiaomi",
|
|
"device-version": "10",
|
|
"channel": "Breo",
|
|
"version_code": "30201",
|
|
"version": "3.2.1",
|
|
"encrypt": "1",
|
|
"Content-Type": "application/json; charset=UTF-8",
|
|
"Referer": "https://servicewechat.com/wx61457400e4212cec/390/page-frame.html",
|
|
"User-Agent": DEFAULT_UA,
|
|
}
|
|
|
|
def get_wx_code(account_id):
|
|
if not WX_AUTH:
|
|
raise RuntimeError("缺少wx_auth,无法从wx_server获取code")
|
|
resp = session.post(
|
|
f"{WX_SERVER_URL}/wx/code",
|
|
json={"appid": MINI_APP_ID, "openid": account_id},
|
|
headers=wx_headers(),
|
|
timeout=30,
|
|
)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
code = data.get("code") or (data.get("data") or {}).get("code")
|
|
if not code:
|
|
raise RuntimeError(f"wx_server未返回code: {data}")
|
|
return code
|
|
|
|
def login_with_code(account_id):
|
|
code = get_wx_code(account_id)
|
|
login_resp = session.get(
|
|
f"{LOGIN_BASE}/login/{code}",
|
|
headers=app_headers(),
|
|
timeout=30,
|
|
)
|
|
login_resp.raise_for_status()
|
|
login_data = login_resp.json()
|
|
if str(login_data.get("code")) != "200" or not login_data.get("data"):
|
|
raise RuntimeError(f"code登录失败: {login_data}")
|
|
user_info = login_data["data"]
|
|
uid = user_info.get("uid")
|
|
open_id = user_info.get("openId")
|
|
union_id = user_info.get("unionId")
|
|
if not uid:
|
|
raise RuntimeError(f"登录响应缺少uid: {login_data}")
|
|
|
|
token_resp = session.post(
|
|
f"{APP_BASE}/customer/loginByUid",
|
|
json={"uid": uid, "openId": open_id, "unionId": union_id},
|
|
headers=app_headers(),
|
|
timeout=30,
|
|
)
|
|
token_resp.raise_for_status()
|
|
token_data = token_resp.json()
|
|
if not is_success(token_data) or not (token_data.get("result") or {}).get("token"):
|
|
raise RuntimeError(f"业务token登录失败: {token_data}")
|
|
return {
|
|
"token": token_data["result"]["token"],
|
|
"uid": uid,
|
|
"openId": open_id,
|
|
"unionId": union_id,
|
|
"userInfo": user_info,
|
|
"customer": token_data.get("result", {}),
|
|
"updatedAt": int(time.time()),
|
|
}
|
|
|
|
def get_cached_token(account_id):
|
|
return read_token_cache().get(account_id)
|
|
|
|
def save_cached_token(account_id, auth_info):
|
|
cache = read_token_cache()
|
|
cache[account_id] = auth_info
|
|
write_token_cache(cache)
|
|
|
|
def remove_cached_token(account_id):
|
|
cache = read_token_cache()
|
|
if account_id in cache:
|
|
del cache[account_id]
|
|
write_token_cache(cache)
|
|
|
|
def validate_token(token):
|
|
try:
|
|
resp = session.post(
|
|
f"{APP_BASE}/getUserLevelInfoByUid",
|
|
headers=app_headers(token),
|
|
timeout=15,
|
|
)
|
|
if resp.status_code != 200:
|
|
return False
|
|
data = resp.json()
|
|
if is_success(data):
|
|
return True
|
|
return not is_token_error(data)
|
|
except Exception:
|
|
return False
|
|
|
|
def get_token_for_account(account_id, index):
|
|
cached = get_cached_token(account_id)
|
|
if cached and cached.get("token"):
|
|
print(f"账号 {index} 使用缓存token: {mask_token(cached['token'])}")
|
|
if validate_token(cached["token"]):
|
|
return cached["token"]
|
|
print(f"账号 {index} 缓存token失效,重新登录")
|
|
remove_cached_token(account_id)
|
|
|
|
auth_info = login_with_code(account_id)
|
|
save_cached_token(account_id, auth_info)
|
|
user_info = auth_info.get("userInfo") or {}
|
|
print(f"账号 {index} code登录成功: {user_info.get('nickname') or user_info.get('telephone') or auth_info.get('uid')}")
|
|
return auth_info["token"]
|
|
|
|
def get_random_one_word():
|
|
try:
|
|
response = requests.get("https://uapis.cn/api/say")
|
|
if response.status_code == 200:
|
|
return response.text.strip()
|
|
else:
|
|
return "无法获取一言"
|
|
except Exception as e:
|
|
print(f"获取一言时出错: {e}")
|
|
return "无法获取一言"
|
|
|
|
def get_proclamation():
|
|
primary_url = "https://github.com/3288588344/toulu/raw/refs/heads/main/tl.txt"
|
|
backup_url = "https://tfapi.cn/TL/tl.json"
|
|
try:
|
|
response = requests.get(primary_url, timeout=10)
|
|
if response.status_code == 200:
|
|
print("\n" + "=" * 50)
|
|
print("📢 公告信息")
|
|
print("=" * 35)
|
|
print(response.text)
|
|
print("=" * 35 + "\n")
|
|
print("公告获取成功,开始执行任务...\n")
|
|
return
|
|
except requests.exceptions.RequestException as e:
|
|
print(f"获取公告时发生错误: {e}, 尝试备用链接...")
|
|
|
|
try:
|
|
response = requests.get(backup_url, timeout=10)
|
|
if response.status_code == 200:
|
|
print("\n" + "=" * 50)
|
|
print("📢 公告信息")
|
|
print("=" * 35)
|
|
print(response.text)
|
|
print("=" * 35 + "\n")
|
|
print("公告获取成功,开始执行任务...\n")
|
|
else:
|
|
print(f"⚠️ 获取公告失败,状态码: {response.status_code}")
|
|
except requests.exceptions.RequestException as e:
|
|
print(f"⚠️ 获取公告时发生错误: {e}, 可能是网络问题或链接无效。")
|
|
|
|
def post_to_breo(token, content, title):
|
|
url = "https://breoplus.breo.cn/breo-app/communityBaseInfo/releasePost"
|
|
headers = breo_task_headers(token)
|
|
data = {
|
|
"anonymoused": 1,
|
|
"content": content,
|
|
"expressText": "",
|
|
"images": [],
|
|
"subTitle": "",
|
|
"title": title,
|
|
"topicText": ""
|
|
}
|
|
try:
|
|
response = requests.post(url, headers=headers, data=json.dumps(data))
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
if result.get("success", False):
|
|
print("✅ 发帖成功!")
|
|
print(f"帖子 ID: {result['result']['id']}")
|
|
print(f"帖子标题: {result['result']['title']}")
|
|
return result["result"]["id"]
|
|
else:
|
|
print(f"❌ 发帖失败,错误信息:{result.get('message', '未知错误')}")
|
|
return None
|
|
else:
|
|
print(f"❌ 请求失败,状态码:{response.status_code}")
|
|
return None
|
|
except Exception as e:
|
|
print(f"❌ 请求错误: {e}")
|
|
return None
|
|
|
|
def collect_post(token, post_id):
|
|
url = "https://breoplus.breo.cn/breo-app/communityBaseInfo/collect"
|
|
headers = breo_task_headers(token)
|
|
data = {
|
|
"postId": post_id
|
|
}
|
|
try:
|
|
response = requests.post(url, headers=headers, data=json.dumps(data))
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
if result.get("success", False):
|
|
print("✅ 收藏成功!")
|
|
reward = result.get("result") or {}
|
|
print(f"获得点数: {reward.get('point', 0)}")
|
|
print(f"成长值: {reward.get('grow', 0)}")
|
|
else:
|
|
print(f"❌ 收藏失败,错误信息:{result.get('message', '未知错误')}")
|
|
else:
|
|
print(f"❌ 请求失败,状态码:{response.status_code}")
|
|
except Exception as e:
|
|
print(f"❌ 请求错误: {e}")
|
|
|
|
def comment_post(token, post_id):
|
|
for _ in range(2): # 评论2次
|
|
comment_content = get_random_one_word() # 使用随机一言作为评论内容
|
|
url = "https://breoplus.breo.cn/breo-app/communityBaseInfo/comment"
|
|
headers = breo_task_headers(token)
|
|
data = {
|
|
"anonymoused": 0,
|
|
"commentText": comment_content,
|
|
"postId": post_id
|
|
}
|
|
try:
|
|
response = requests.post(url, headers=headers, data=json.dumps(data))
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
if result.get("success", False):
|
|
print("✅ 评论成功!")
|
|
reward = result.get("result") or {}
|
|
root = reward.get("rootOutVO") or {}
|
|
print(f"评论内容: {root.get('commentText', comment_content)}")
|
|
print(f"获得点数: {reward.get('point', 0)}")
|
|
print(f"成长值: {reward.get('grow', 0)}")
|
|
else:
|
|
print(f"❌ 评论失败,错误信息:{result.get('message', '未知错误')}")
|
|
else:
|
|
print(f"❌ 请求失败,状态码:{response.status_code}")
|
|
except Exception as e:
|
|
print(f"❌ 请求错误: {e}")
|
|
time.sleep(1) # 避免频繁请求
|
|
|
|
def browse_mall(token):
|
|
url = "https://breoplus.breo.cn/breo-app/user/po-task-info/mall"
|
|
headers = breo_task_headers(token)
|
|
try:
|
|
response = requests.post(url, headers=headers)
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
if result.get("success", False):
|
|
print("✅ 浏览商城成功!")
|
|
reward = result.get("result") or {}
|
|
print(f"获得点数: {reward.get('point', 0)}")
|
|
print(f"成长值: {reward.get('grow', 0)}")
|
|
else:
|
|
print(f"❌ 浏览商城失败,错误信息:{result.get('message', '未知错误')}")
|
|
else:
|
|
print(f"❌ 请求失败,状态码:{response.status_code}")
|
|
except Exception as e:
|
|
print(f"❌ 请求错误: {e}")
|
|
|
|
def punch_in(token):
|
|
url = "https://breoplus.breo.cn/breo-app/user/po-task-info/punch"
|
|
headers = breo_task_headers(token)
|
|
try:
|
|
response = requests.post(url, headers=headers)
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
if result.get("success", False):
|
|
print("✅ 签到成功!")
|
|
reward = result.get("result") or {}
|
|
print(f"获得点数: {reward.get('point', 0)}")
|
|
print(f"成长值: {reward.get('grow', 0)}")
|
|
else:
|
|
print(f"❌ 签到失败,错误信息:{result.get('message', '未知错误')}")
|
|
else:
|
|
print(f"❌ 请求失败,状态码:{response.status_code}")
|
|
except Exception as e:
|
|
print(f"❌ 请求错误: {e}")
|
|
|
|
if __name__ == "__main__":
|
|
# 获取公告
|
|
#get_proclamation()
|
|
|
|
# 从环境变量读取 wx_server 账号标识/openid
|
|
accounts = [item.strip() for item in os.getenv("BREO", "").replace("&", "\n").splitlines() if item.strip()]
|
|
|
|
if not accounts:
|
|
print("❌ 未检测到 账号信息,退出脚本。")
|
|
else:
|
|
skip_community = os.getenv("BREO_SKIP_COMMUNITY", "").lower() in ("1", "true", "yes")
|
|
print("=============== 开始执行任务 ===============")
|
|
for i, account in enumerate(accounts, 1):
|
|
print(f"\n-------------- 账号 {i} 开始 --------------")
|
|
try:
|
|
token = get_token_for_account(account, i)
|
|
except Exception as e:
|
|
print(f"❌ 账号 {i} 登录失败: {e}")
|
|
continue
|
|
|
|
print("🚀 正在签到...")
|
|
punch_in(token)
|
|
|
|
if skip_community:
|
|
print("\n📝 已跳过发帖/收藏/评论任务")
|
|
else:
|
|
print("\n📝 正在发布帖子...")
|
|
post_id = post_to_breo(token, "这是一个自动发布的帖子", "自动化测试")
|
|
if post_id:
|
|
print("\n⭐ 正在收藏帖子...")
|
|
collect_post(token, post_id)
|
|
|
|
print("\n💬 正在评论帖子...")
|
|
comment_post(token, post_id)
|
|
else:
|
|
print("❌ 发帖失败,跳过后续操作。")
|
|
|
|
print("\n🛒 正在浏览商城...")
|
|
browse_mall(token)
|
|
|
|
print(f"-------------- 账号 {i} 结束 --------------")
|
|
|
|
print("\n=============== 所有任务执行完毕 ===============")
|