This commit is contained in:
smallfawn committed 2026-06-01 14:25:13 +08:00
1 parent 4babb0ba12
commit 7d375a3858
10 files changed

No files matched your search

-393
View File
@@ -1,393 +0,0 @@
#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=============== 所有任务执行完毕 ===============")
-281
View File
@@ -1,281 +0,0 @@
/*
爱玛会员俱乐部 - 自动签到脚本
变量名:aima
变量值:账号标识/openid(支持多账号,用 & 或换行分隔)
CODE登录依赖:wx_server_url、wx_auth
*/
const { Env } = require("../tools/env");
const $ = new Env("爱玛会员俱乐部");
const axios = require("axios");
const crypto = require("crypto");
const fs = require("fs");
const path = require("path");
// ================== 配置区 ==================
const ACTIVITY_ID = "100001192";
const BASE_URL = "https://scrm.aimatech.com";
const WXCLIENT_URL = `${BASE_URL}/aima/wxclient`;
const MINI_APPID = "wx2dcfb409fd5ddfb4";
const APP_ID = "scrm";
const TOKEN_CACHE_FILE = path.join(__dirname, "aima_token_cache.json");
const USER_AGENT =
"Mozilla/5.0 (Linux; Android 15; 23013RK75C Build/AQ3A.250226.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.173 Mobile Safari/537.36 XWEB/1420229 MMWEBSDK/20251101 MMWEBID/6369 MicroMessenger/8.0.67.3000(0x28004333) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64 MiniProgramEnv/android";
// ================== 工具函数 ==================
function generateUUID() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
const v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
function md5(str) {
return crypto.createHash("md5").update(str).digest("hex");
}
function maskToken(token = "") {
if (!token) return "";
if (token.length <= 12) return `${token.slice(0, 3)}***`;
return `${token.slice(0, 6)}***${token.slice(-6)}`;
}
function isToken(value = "") {
return /^eyJ/.test(value.trim()) || value.length > 120;
}
function readCache() {
try {
if (!fs.existsSync(TOKEN_CACHE_FILE)) return {};
return JSON.parse(fs.readFileSync(TOKEN_CACHE_FILE, "utf8"));
} catch (e) {
return {};
}
}
function writeCache(cache) {
try {
fs.writeFileSync(TOKEN_CACHE_FILE, JSON.stringify(cache, null, 2));
} catch (e) {
$.log(`⚠️ token缓存写入失败: ${e.message}`);
}
}
function updateCachedToken(account, token) {
if (!account || !token || isToken(account)) return;
const cache = readCache();
cache[account] = {
token,
updatedAt: new Date().toISOString(),
};
writeCache(cache);
}
function getResponseToken(headers = {}) {
return (
headers["set-access-token"] ||
headers["Set-Access-Token"] ||
headers["SET-ACCESS-TOKEN"] ||
""
);
}
function buildHeaders(token = "") {
const timestamp = Date.now().toString();
const traceLogId = generateUUID();
const signToken = token ? token.substring(50, 80) : "";
const signStr = `App-Id${APP_ID}Time-Stamp${timestamp}TraceLog-Id${traceLogId}Access-Token${signToken}AimaScrm321_^`;
return {
"App-Id": APP_ID,
"Time-Stamp": timestamp,
"TraceLog-Id": traceLogId,
"Access-Token": token,
Sign: md5(signStr).toLowerCase(),
"content-type": "application/json",
charset: "utf-8",
Referer: "https://servicewechat.com/wx2dcfb409fd5ddfb4/223/page-frame.html",
"User-Agent": USER_AGENT,
};
}
async function request(method, url, token, options = {}) {
const res = await axios({
method,
url,
headers: buildHeaders(token),
timeout: 15000,
validateStatus: () => true,
...options,
});
const newToken = getResponseToken(res.headers);
if (newToken && options.account) {
options.onToken?.(newToken);
updateCachedToken(options.account, newToken);
}
return res;
}
async function getWxCode(account) {
const wxServerUrl = process.env.wx_server_url;
const wxAuth = process.env.wx_auth;
if (!wxServerUrl || !wxAuth) {
throw new Error("未配置 wx_server_url 或 wx_auth,无法获取code登录");
}
const res = await axios.post(
`${wxServerUrl.replace(/\/$/, "")}/wx/operatedata`,
{
appid: MINI_APPID,
openid: account,
},
{
headers: {
auth: wxAuth,
"content-type": "application/json",
},
timeout: 15000,
validateStatus: () => true,
}
);
const code = res.data?.code || res.data?.data?.code;
if (res.status !== 200 || !code) {
throw new Error(`获取code失败: HTTP ${res.status} ${JSON.stringify(res.data)}`);
}
return code;
}
async function loginByCode(account) {
$.log("🔐 正在获取code并登录...");
const code = await getWxCode(account);
const res = await request("post", `${WXCLIENT_URL}/user/members:login`, "", {
data: { code },
account,
});
const token = getResponseToken(res.headers);
if (res.status !== 200 || res.data?.code !== 200 || !token) {
throw new Error(`登录失败: HTTP ${res.status} ${JSON.stringify(res.data)}`);
}
updateCachedToken(account, token);
$.log(`✅ 登录成功,已缓存token: ${maskToken(token)}`);
return token;
}
async function validateToken(account, token) {
const res = await request("get", `${WXCLIENT_URL}/member/IndexInfo`, token, {
account,
});
const ok = res.status === 200 && res.data?.code === 200;
if (!ok) {
$.log(`⚠️ 缓存token失效: HTTP ${res.status} ${JSON.stringify(res.data)}`);
}
return ok;
}
async function getAccessToken(account) {
const value = account.trim();
if (isToken(value)) {
$.log(`ℹ️ 检测到旧token变量,将直接使用: ${maskToken(value)}`);
return value;
}
const cache = readCache();
const cachedToken = cache[value]?.token;
if (cachedToken) {
$.log(`🔎 使用缓存token校验: ${maskToken(cachedToken)}`);
if (await validateToken(value, cachedToken)) {
$.log("✅ 缓存token有效");
return cachedToken;
}
}
return loginByCode(value);
}
// ================== 核心逻辑 ==================
async function signIn(account, index) {
let token = await getAccessToken(account);
const setToken = (newToken) => {
token = newToken;
};
$.log(`🚀 账号【${index}】查询签到状态...`);
const searchRes = await request(
"post",
`${WXCLIENT_URL}/mkt/activities/sign:search`,
token,
{
data: { activityId: ACTIVITY_ID },
account,
onToken: setToken,
}
);
if (searchRes.status !== 200 || searchRes.data?.code !== 200) {
throw new Error(`查询签到状态失败: HTTP ${searchRes.status} ${JSON.stringify(searchRes.data)}`);
}
const signStatus = searchRes.data?.content?.signStatus;
if (signStatus === 1) {
$.log(`✅ 账号【${index}】今日已签到!`);
return;
}
$.log(`⏳ 账号【${index}】正在签到...`);
const joinRes = await request(
"post",
`${WXCLIENT_URL}/mkt/activities/sign:join`,
token,
{
data: { activityId: ACTIVITY_ID, activitySceneId: null },
account,
onToken: setToken,
}
);
if (joinRes.status === 200 && joinRes.data?.code === 200) {
const point = joinRes.data.content?.point || joinRes.data.content?.points || 0;
$.log(`🎉 账号【${index}】签到成功!${point ? `获得 ${point} 积分` : ""}`);
} else {
throw new Error(`签到失败: HTTP ${joinRes.status} ${JSON.stringify(joinRes.data)}`);
}
}
// ================== 主函数 ==================
!(async () => {
console.log("\n🔔 爱玛会员俱乐部, 开始!");
let accounts = [];
if ($.isNode()) {
const env = process.env.aima;
if (env) {
accounts = env.split(/&|\n/).map((t) => t.trim()).filter(Boolean);
}
}
if (accounts.length === 0) {
$.msg("❌ 未找到账号标识,请配置变量 'aima'");
return;
}
console.log(`共找到${accounts.length}个账号`);
for (let i = 0; i < accounts.length; i++) {
try {
console.log(`\n🚀 user:【${i + 1}】 start work`);
await signIn(accounts[i], i + 1);
} catch (e) {
console.log(`❌ 账号【${i + 1}】执行失败: ${e.message}`);
}
}
// await $.sendMsg($.logs.join("\n"));
})()
.catch((e) => console.log(e))
.finally(() => $.done());
-340
View File
@@ -1,340 +0,0 @@
/*
------------------------------------------
@Author: sm
@Date: 2026.06.01
@Description: 七彩虹微信小程序 签到&点赞
cron: 30 10 * * *
------------------------------------------
#Notice:
变量名:colorful
变量值:wx_server 里的 openid/账号标识,多账户&或换行
需要配置:wx_server_url、wx_auth
说明:小程序当前登录链路为 wx.login code -> /api/User/OnLogin 获取 OpenId,
首次换取业务 Token 还需要微信手机号授权 code;现有 wx_server 只能提供 wx.login code。
脚本会复用本地 token 缓存,缓存失效或首次运行时会自动验证 code 登录能力并给出明确原因。
⚠️【免责声明】
------------------------------------------
1、此脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。
2、由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。
3、请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。
4、此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。
5、本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。
6、如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此脚本。
7、所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。
*/
const { Env } = require("../tools/env.js");
const $ = new Env("colorful七彩虹");
const axios = require("axios");
const crypto = require("crypto");
const fs = require("fs");
const path = require("path");
const WeChatServer = require("../wxapp/wcs.js");
const ckName = "colorful";
const strSplitor = "#";
const MINI_APP_ID = "wx49018277e65fc3e1";
const PAGE_VERSION = "91";
const API_BASE = "https://interface.skycolorful.com";
const TOKEN_CACHE_FILE = path.join(__dirname, "colorful_token_cache.json");
const defaultUserAgent =
"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 WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf254173b) XWEB/19027";
const wechat = new WeChatServer({
url: process.env.wx_server_url || "http://192.168.31.196:8787",
appid: MINI_APP_ID,
auth: process.env.wx_auth || "",
});
function readTokenCache() {
try {
if (!fs.existsSync(TOKEN_CACHE_FILE)) return {};
return JSON.parse(fs.readFileSync(TOKEN_CACHE_FILE, "utf8")) || {};
} catch (e) {
return {};
}
}
function writeTokenCache(cache) {
try {
fs.writeFileSync(TOKEN_CACHE_FILE, JSON.stringify(cache, null, 2), "utf8");
} catch (e) {
$.log(`写入token缓存失败: ${e.message || e}`);
}
}
function shortToken(token = "") {
const value = String(token || "").replace(/^Bearer\s+/i, "");
return value ? `${value.slice(0, 6)}***${value.slice(-6)}` : "";
}
function maskPhone(phone = "") {
return String(phone).replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2");
}
function md5(str) {
return crypto.createHash("md5").update(str).digest("hex");
}
function isTokenError(message) {
return /401|403|token|登录|授权|未登录|无效|过期|失效/i.test(String(message || ""));
}
function isSuccess(result) {
return Number(result?.Code) === 0 && result?.Success !== false;
}
class Task {
constructor(env) {
this.index = $.userIdx++;
this.raw = String(env || "").trim();
this.user = this.raw.split(strSplitor);
this.accountId = this.user[0].trim();
this.token = "";
this.refreshToken = "";
this.openId = "";
this.userInfo = {};
this.signStatus = false;
this.isLegacyToken = this.user.length >= 2;
if (this.isLegacyToken) {
this.token = this.user[0].trim();
this.refreshToken = this.user[1].trim();
this.accountId = `legacy:${shortToken(this.token)}`;
}
}
async run() {
if (this.isLegacyToken) {
$.log(`账号[${this.index}] 检测到旧版 token 格式,将仅用于迁移缓存`);
this.saveCachedToken();
} else {
const cached = this.getCachedToken();
if (cached?.token) {
this.applyToken(cached);
$.log(`账号[${this.index}] 使用缓存token: ${shortToken(this.token)}`);
if (!(await this.checkToken())) {
this.removeCachedToken();
$.log(`账号[${this.index}] 缓存token失效,重新尝试CODE登录`);
}
}
if (!this.token) {
await this.loginByWxCode();
if (!this.token) return;
}
}
await this.getUserInfo();
await this.getSignInfo();
if (this.signStatus) {
$.log(`账号[${this.index}] 今日已签到`);
return;
}
await this.signInV2();
}
cacheKey() {
return this.isLegacyToken ? this.accountId : this.raw;
}
getCachedToken() {
const cache = readTokenCache();
return cache[this.cacheKey()] || null;
}
saveCachedToken() {
if (!this.token) return;
const cache = readTokenCache();
cache[this.cacheKey()] = {
token: this.token,
refreshToken: this.refreshToken,
openId: this.openId,
userInfo: this.userInfo,
updatedAt: new Date().toISOString(),
};
writeTokenCache(cache);
}
removeCachedToken() {
const cache = readTokenCache();
if (cache[this.cacheKey()]) {
delete cache[this.cacheKey()];
writeTokenCache(cache);
}
this.token = "";
this.refreshToken = "";
this.userInfo = {};
}
applyToken(data = {}) {
this.token = data.token || data.Token || "";
this.refreshToken = data.refreshToken || data.RefreshToken || "";
this.openId = data.openId || data.OpenId || "";
this.userInfo = data.userInfo || data.UserInfo || {};
}
signedHeaders(extra = {}, auth = true) {
const appid = "815d8026-9a52-4445-a42c-a5443134232e";
const requestId = crypto.randomUUID ? crypto.randomUUID() : $.uuid();
const ticks = Date.now();
const headers = {
"User-Agent": defaultUserAgent,
"Referer": `https://servicewechat.com/${MINI_APP_ID}/${PAGE_VERSION}/page-frame.html`,
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Content-Type": "application/json",
"requestId": requestId,
"AppId": appid,
"Ticks": String(ticks),
"Sign": md5(appid + ticks + requestId + "2b5c01fb-7640-401a-8188-43a13190a626"),
"source": "Wx",
"UcSource": "30",
"User-from": "xcx",
"version": "2.0.0",
"xweb_xhr": "1",
...extra,
};
if (auth) {
headers.Authorization = this.token ? `Bearer ${this.token}` : "";
headers["X-Authorization"] = this.refreshToken ? `Bearer ${this.refreshToken}` : "";
}
return headers;
}
async request(apiPath, { method = "GET", data, params, auth = true } = {}) {
const options = {
method,
url: new URL(apiPath, API_BASE).toString(),
params,
headers: this.signedHeaders({}, auth),
timeout: 15000,
validateStatus: () => true,
};
if (data !== undefined) options.data = data;
const { data: result, status, headers } = await axios.request(options);
if (headers?.["access-token"]) this.token = headers["access-token"];
if (headers?.["x-access-token"]) this.refreshToken = headers["x-access-token"];
if (status === 401 || status === 403) throw new Error(`HTTP ${status}: ${result?.Message || JSON.stringify(result)}`);
if (status !== 200) throw new Error(`HTTP ${status}: ${JSON.stringify(result)}`);
if (!isSuccess(result)) throw new Error(`${result?.Code ?? ""} ${result?.Message || result?.msg || JSON.stringify(result)}`.trim());
if (this.token) this.saveCachedToken();
return result;
}
async getLoginCode() {
if (!process.env.wx_auth) throw new Error("缺少 wx_auth,无法从 wx_server 获取 code");
const { data } = await wechat.getCode(this.raw);
const code = data?.code || data?.data?.code;
if (!code) throw new Error(`wx_server 未返回 code: ${JSON.stringify(data)}`);
return code;
}
async loginByWxCode() {
try {
const code = await this.getLoginCode();
const result = await this.request("/api/User/OnLogin", {
method: "POST",
auth: false,
data: { Code: code },
});
this.openId = result?.Data?.OpenId || "";
const token = result?.Data?.Token || result?.Data?.token || "";
const refreshToken = result?.Data?.RefreshToken || result?.Data?.refreshToken || "";
if (token) {
this.token = token;
this.refreshToken = refreshToken;
this.saveCachedToken();
$.log(`账号[${this.index}] CODE登录成功: ${shortToken(this.token)}`);
return;
}
throw new Error(`OnLogin仅返回OpenId=${this.openId || "空"},首次业务登录仍需要微信手机号授权code`);
} catch (e) {
$.log(`账号[${this.index}] CODE登录失败: ${e.message || e}`);
}
}
async checkToken() {
try {
await this.request("/api/User/RefreshLoginTime", {
method: "POST",
data: { phone: "" },
});
return true;
} catch (e) {
return false;
}
}
async signInV2() {
try {
const result = await this.request("/api/User/SignV2", {
method: "POST",
data: {},
});
$.log(`🌸账号[${this.index}]🕊签到${result.Message || "成功"}🎉`);
} catch (e) {
const message = String(e.message || e);
if (/已签到|已签|重复/.test(message)) {
$.log(`🌸账号[${this.index}] 今日已签到`);
return;
}
$.log(`🌸账号[${this.index}] 签到失败:${message}❌`);
if (isTokenError(message)) this.removeCachedToken();
}
}
async getSignInfo() {
try {
const result = await this.request("/api/User/IsSignV2");
this.signStatus = Boolean(result?.Data?.IsSign);
} catch (e) {
const message = String(e.message || e);
$.log(`账号[${this.index}] 查询签到状态失败: ${message}`);
if (isTokenError(message)) this.removeCachedToken();
}
}
async getUserInfo() {
try {
const result = await this.request("/api/User/GetUserInfo");
this.userInfo = result?.Data || {};
this.saveCachedToken();
$.log(`🌸账号[${this.index}]昵称:${this.userInfo.NickName || maskPhone(this.userInfo.Mobile) || "未知"} 积分:${this.userInfo.Point ?? "未知"}`);
} catch (e) {
const message = String(e.message || e);
$.log(`账号[${this.index}] 查询用户失败: ${message}`);
if (isTokenError(message)) this.removeCachedToken();
}
}
}
// 暂停主运行:当前七彩虹后端首次换取业务 Token 需要微信手机号授权 code,
// 现有 wx_server 只能提供 wx.login code,恢复前不要自动执行任务。
// !(async () => {
// await getNotice();
// $.checkEnv(ckName);
//
// for (const user of $.userList) {
// await new Task(user).run();
// }
// })()
// .catch((e) => console.log(e))
// .finally(() => $.done());
async function getNotice() {
try {
const options = {
url: "https://ghproxy.net/https://raw.githubusercontent.com/smallfawn/Note/refs/heads/main/Notice.json",
headers: {
"User-Agent": defaultUserAgent,
},
timeout: 3000,
};
const { data: res } = await axios.request(options);
$.log(res);
return res;
} catch (e) {}
}
-288
View File
@@ -1,288 +0,0 @@
/*
------------------------------------------
@Author: sm
@Date: 2026.06.01
@Description: fsdlb 微信小程序逢三得利吧 签到积分
cron: 30 9 * * *
------------------------------------------
变量名:fsdlb
变量值:wx_server 里的 openid/账号标识,多账号用 & 或换行
需要配置:wx_server_url、wx_auth
⚠️【免责声明】
------------------------------------------
1、此脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。
2、由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。
3、请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。
4、此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。
5、本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。
6、如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此脚本。
7、所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。
*/
const { Env } = require("../tools/env.js");
const $ = new Env("逢三得利吧小程序");
const axios = require("axios");
const fs = require("fs");
const path = require("path");
const WeChatServer = require("../wxapp/wcs.js");
let ckName = "fsdlb";
const MINI_APP_ID = "wxb33ed03c6c715482";
const MERCHANT_APP_NAME = "20230130307725";
const TEMPLATE_VERSION = "0.6.4";
const X_VERSION = "2.3.5";
const CLIENT_ID = "saas-wechat-app";
const APP_PUBLISH_TYPE = 1;
const MINIAPP_ID = 159;
const API_BASE = "https://xiaodian.miyatech.com/api";
const TOKEN_CACHE_FILE = path.join(__dirname, "fsdlb_token_cache.json");
const USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) MicroMessenger/3.9.12 MiniProgramEnv/Windows WindowsWechat/WMPF";
const defaultUserAgent = USER_AGENT;
const wechat = new WeChatServer({
url: process.env.wx_server_url || "http://192.168.31.196:8787",
appid: MINI_APP_ID,
auth: process.env.wx_auth || "",
});
function readTokenCache() {
try {
if (!fs.existsSync(TOKEN_CACHE_FILE)) return {};
return JSON.parse(fs.readFileSync(TOKEN_CACHE_FILE, "utf8")) || {};
} catch (e) {
return {};
}
}
function writeTokenCache(cache) {
try {
fs.writeFileSync(TOKEN_CACHE_FILE, JSON.stringify(cache, null, 2), "utf8");
} catch (e) {
$.log(`写入token缓存失败: ${e.message || e}`);
}
}
function shortToken(token = "") {
const value = String(token).replace(/^bearer\s+/i, "");
return value ? `${value.slice(0, 6)}***${value.slice(-6)}` : "";
}
function maskPhone(phone = "") {
return String(phone).replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2");
}
function pickToken(data = {}) {
return data?.tokenInfo?.access_token || data.accessToken || data.access_token || data.token || "";
}
function isSuccess(result) {
return String(result?.code) === "200";
}
function isTokenError(message) {
return /401|403|M401|M403|token|登录|授权|未登录|无效|过期/i.test(String(message || ""));
}
class Task {
constructor(openid) {
this.index = $.userIdx++;
this.openid = String(openid || "").trim();
this.token = "";
this.userInfo = {};
}
async run() {
const cached = this.getCachedToken();
if (cached) {
this.applyToken(cached);
$.log(`账号[${this.index}] 使用缓存token: ${shortToken(this.token)}`);
if (!(await this.checkToken())) {
this.removeCachedToken();
$.log(`账号[${this.index}] 缓存token失效,重新登录`);
}
}
if (!this.token) {
await this.loginByWxCode();
if (!this.token) return;
}
await this.info();
await this.signIn();
}
getCachedToken() {
const cache = readTokenCache();
return cache[this.openid] || null;
}
saveCachedToken() {
if (!this.token) return;
const cache = readTokenCache();
cache[this.openid] = {
accessToken: this.token,
userInfo: this.userInfo,
updatedAt: new Date().toISOString(),
};
writeTokenCache(cache);
}
removeCachedToken() {
const cache = readTokenCache();
if (cache[this.openid]) {
delete cache[this.openid];
writeTokenCache(cache);
}
this.token = "";
this.userInfo = {};
}
applyToken(data = {}) {
this.token = pickToken(data);
this.userInfo = data.userInfo || data.thirdUsrIf || {};
}
getHeaders(extra = {}, auth = true) {
const headers = {
componentSend: "1",
"HH-FROM": MERCHANT_APP_NAME,
"HH-APP": MINI_APP_ID,
"HH-VERSION": TEMPLATE_VERSION,
"X-VERSION": X_VERSION,
"Content-Type": "application/json",
appPublishType: APP_PUBLISH_TYPE,
"HH-CI": CLIENT_ID,
"MARKETING-PLAN-NO": "",
"USER-ENTRANCE-CHANNEL": "",
"USER-ENTRANCE-CHANNEL-KEY": "",
"ONE-ID": this.userInfo.oneId || "",
groupPosId: "",
"User-Agent": USER_AGENT,
...extra,
};
if (auth && this.token) headers.Authorization = `bearer ${this.token}`;
return headers;
}
async request({ method = "GET", path: apiPath, data, params, auth = true }) {
const options = {
method,
url: `${API_BASE}${apiPath.startsWith("/") ? apiPath : `/${apiPath}`}`,
headers: this.getHeaders({}, auth),
timeout: 15000,
validateStatus: () => true,
};
if (params) options.params = params;
if (data !== undefined) options.data = data;
const { data: result, status } = await axios.request(options);
if (status !== 200) throw new Error(`HTTP ${status}: ${JSON.stringify(result)}`);
if (!isSuccess(result)) throw new Error(result?.msg || JSON.stringify(result));
return result.data;
}
async getLoginCode() {
const { data } = await wechat.getCode(this.openid);
const code = data?.code || data?.data?.code;
if (!code) throw new Error(`wx_server 未返回 code: ${JSON.stringify(data)}`);
return code;
}
async loginByWxCode() {
try {
const code = await this.getLoginCode();
const data = await this.request({
method: "POST",
path: "/user/login/wx-jc",
auth: false,
data: {
jsCode: code,
clientId: CLIENT_ID,
myUnionId: "",
appPublishType: APP_PUBLISH_TYPE,
},
});
this.token = pickToken(data);
this.userInfo = data?.thirdUsrIf || {};
if (!this.token) throw new Error(`登录响应未返回token: ${JSON.stringify(data)}`);
this.saveCachedToken();
$.log(`账号[${this.index}] 登录成功: ${maskPhone(this.userInfo.phone) || this.userInfo.oid || ""}`);
} catch (e) {
$.log(`账号[${this.index}] 登录失败: ${e.message || e}`);
}
}
async checkToken() {
try {
await this.request({
path: "/user/auth/member/integral/union/flow/list",
params: { pageNo: 1, pageSize: 1, dataType: "SCORE" },
});
return true;
} catch (e) {
return false;
}
}
async signIn() {
try {
const data = await this.request({
method: "POST",
path: "/coupon/auth/signIn",
data: {
miniappId: MINIAPP_ID,
},
});
$.log(`🕊账号[${this.index}] 签到成功:${data?.integralToastText || "成功"}🎉`);
} catch (e) {
const message = String(e.message || e);
if (/已签到|已签|重复/.test(message)) {
$.log(`🕊账号[${this.index}] 今日已签到`);
return;
}
$.log(`🕊账号[${this.index}] 签到失败:${message}🚫`);
if (isTokenError(message)) this.removeCachedToken();
}
}
async info() {
try {
const data = await this.request({
path: "/user/auth/member/integral/union/flow/list",
params: { pageNo: 1, pageSize: 10, dataType: "SCORE" },
});
$.log(`🕊账号[${this.index}] 查询成功:总积分[${data?.totalScore ?? "未知"}]🎉`);
} catch (e) {
const message = String(e.message || e);
$.log(`🕊账号[${this.index}] 查询失败:${message}🚫`);
if (isTokenError(message)) this.removeCachedToken();
}
}
}
!(async () => {
await getNotice();
$.checkEnv(ckName);
for (const openid of $.userList) {
await new Task(openid).run();
}
})()
.catch((e) => $.log(e.message || e))
.finally(() => $.done());
async function getNotice() {
try {
const options = {
url: "https://ghproxy.net/https://raw.githubusercontent.com/smallfawn/Note/refs/heads/main/Notice.json",
headers: {
"User-Agent": defaultUserAgent,
},
timeout: 3000,
};
const { data: res } = await axios.request(options);
$.log(res);
return res;
} catch (e) { }
}
-288
View File
@@ -1,288 +0,0 @@
/*
------------------------------------------
@Author: sm
@Date: 2026.06.01
@Description: 国乐酱酒小程序
cron: 30 9 * * *
------------------------------------------
#Notice:
变量名: gyjj
变量值:wx_server 里的 openid/账号标识,多账号&或换行
需要配置:wx_server_url、wx_auth
⚠️【免责声明】
------------------------------------------
1、此脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。
2、由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。
3、请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。
4、此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。
5、本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。
6、如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此脚本。
7、所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。
*/
const { Env } = require("../tools/env.js");
const $ = new Env("国乐酱酒");
const axios = require("axios");
const fs = require("fs");
const path = require("path");
const ckName = "gyjj";
const strSplitor = "#";
const MINI_APP_ID = "wxeff120e4d11594c0";
const PAGE_VERSION = "87";
const API_BASE = "https://member.guoyuejiu.com/api";
const TOKEN_CACHE_FILE = path.join(__dirname, "gyjj_token_cache.json");
const defaultUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_7_15 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.70(0x1800462d) NetType/WIFI Language/zh_CN";
function readTokenCache() {
try {
if (!fs.existsSync(TOKEN_CACHE_FILE)) return {};
return JSON.parse(fs.readFileSync(TOKEN_CACHE_FILE, "utf8")) || {};
} catch (e) {
return {};
}
}
function writeTokenCache(cache) {
try {
fs.writeFileSync(TOKEN_CACHE_FILE, JSON.stringify(cache, null, 2), "utf8");
} catch (e) {
$.log(`写入token缓存失败: ${e.message || e}`);
}
}
function shortToken(token = "") {
const value = String(token || "").replace(/^Mer/i, "");
return value ? `${value.slice(0, 6)}***${value.slice(-6)}` : "";
}
function isTokenError(message) {
return /1011|401|403|token|登录|授权|失效|过期/i.test(String(message || ""));
}
class Task {
constructor(env) {
this.index = $.userIdx++;
this.raw = String(env || "").trim();
this.user = this.raw.split(strSplitor);
this.accountId = this.user[0].trim();
this.token = "";
this.sessionKey = "";
this.userId = "";
this.userInfo = {};
this.isLegacyToken = /^Mer|eyJ/i.test(this.accountId) || this.user.length >= 2;
if (this.isLegacyToken) {
this.token = this.accountId.replace(/^Mer/i, "");
this.accountId = `legacy:${shortToken(this.token)}`;
}
}
async run() {
if (this.isLegacyToken) {
$.log(`账号[${this.index}] 检测到旧版token格式,先迁移到缓存`);
this.saveCachedToken();
} else {
const cached = this.getCachedToken();
if (cached?.token) {
this.applyToken(cached);
$.log(`账号[${this.index}] 使用缓存token: ${shortToken(this.token)}`);
if (!(await this.checkToken())) {
this.removeCachedToken();
$.log(`账号[${this.index}] 缓存token失效,重新CODE登录`);
}
}
if (!this.token) {
await this.loginByWxCode();
if (!this.token) return;
}
}
await this.sign();
await this.account();
}
cacheKey() {
return this.isLegacyToken ? this.accountId : this.raw;
}
getCachedToken() {
const cache = readTokenCache();
return cache[this.cacheKey()] || null;
}
saveCachedToken() {
if (!this.token) return;
const cache = readTokenCache();
cache[this.cacheKey()] = {
token: this.token,
sessionKey: this.sessionKey,
userId: this.userId,
userInfo: this.userInfo,
updatedAt: new Date().toISOString(),
};
writeTokenCache(cache);
}
removeCachedToken() {
const cache = readTokenCache();
if (cache[this.cacheKey()]) {
delete cache[this.cacheKey()];
writeTokenCache(cache);
}
this.token = "";
this.sessionKey = "";
this.userId = "";
this.userInfo = {};
}
applyToken(data = {}) {
this.token = String(data.token || data.authorization || "").replace(/^Mer/i, "");
this.sessionKey = data.sessionKey || "";
this.userId = data.userId || "";
this.userInfo = data.userInfo || {};
}
headers(extra = {}) {
return {
"content-Type": "application/json",
Authorization: `Mer${this.token}`,
"User-Agent": defaultUserAgent,
Referer: `https://servicewechat.com/${MINI_APP_ID}/${PAGE_VERSION}/page-frame.html`,
...extra,
};
}
async request(pathname, { method = "GET", data, params, auth = true, headers = {} } = {}) {
const options = {
method,
url: `${API_BASE}${pathname}`,
params,
data,
headers: auth ? this.headers(headers) : {
"content-Type": "application/json",
"User-Agent": defaultUserAgent,
Referer: `https://servicewechat.com/${MINI_APP_ID}/${PAGE_VERSION}/page-frame.html`,
...headers,
},
timeout: 20000,
validateStatus: () => true,
};
const { data: result, status } = await axios.request(options);
if (status !== 200) throw new Error(`HTTP ${status}: ${JSON.stringify(result)}`);
if (Number(result?.code) !== 0) throw new Error(`${result?.code ?? ""} ${result?.message || JSON.stringify(result)}`.trim());
return result;
}
async getOperateData() {
if (!process.env.wx_auth) throw new Error("缺少 wx_auth,无法从 wx_server 获取登录数据");
const url = (process.env.wx_server_url || "http://192.168.31.196:8787").replace(/\/$/, "");
const { data } = await axios.post(`${url}/wx/operatedata`, {
appid: MINI_APP_ID,
openid: this.raw,
}, {
headers: { auth: process.env.wx_auth },
timeout: 45000,
validateStatus: () => true,
});
const result = data?.data || {};
if (!data?.status || !result.code) throw new Error(`wx_server 未返回code: ${JSON.stringify(data)}`);
let userInfo = {};
try {
userInfo = JSON.parse(result.data || "{}");
} catch (e) {}
return { code: result.code, userInfo };
}
async loginByWxCode() {
try {
const wxData = await this.getOperateData();
const user = wxData.userInfo || {};
const payload = {
avatarUrl: user.avatarUrl || "",
city: user.city || "",
country: user.country || "",
gender: user.gender || 0,
nickName: user.nickName || user.nickname || "微信用户",
province: user.province || "",
code: wxData.code,
source: 2,
};
const result = await this.request("/user/wxLogin", {
method: "POST",
data: payload,
auth: false,
});
const data = result?.data || {};
if (!data.authorization) throw new Error(`登录响应未返回authorization: ${JSON.stringify(result)}`);
this.token = data.authorization;
this.sessionKey = data.sessionKey || "";
this.userId = data.userId || "";
this.saveCachedToken();
$.log(`账号[${this.index}] CODE登录成功: ${shortToken(this.token)}`);
} catch (e) {
$.log(`账号[${this.index}] CODE登录失败: ${e.message || e}`);
}
}
async checkToken() {
try {
await this.request("/user/info");
return true;
} catch (e) {
return false;
}
}
async sign() {
try {
const result = await this.request("/sign/daily/sign");
$.log(`🕊账号[${this.index}] 签到成功:签到[${result.data?.spanSumDays ?? "未知"}]天🎉`);
} catch (e) {
const message = String(e.message || e);
if (/已签|重复|今日/.test(message)) {
$.log(`🕊账号[${this.index}] 今日已签到`);
return;
}
$.log(`🕊账号[${this.index}] 签到失败:${message}⛔`);
if (isTokenError(message)) this.removeCachedToken();
}
}
async account() {
try {
const result = await this.request("/user/info");
this.userInfo = result.data || {};
this.saveCachedToken();
$.log(`🕊账号[${this.index}] 查询成功:总积分[${result.data?.score ?? "未知"}]🎉`);
} catch (e) {
const message = String(e.message || e);
$.log(`🕊账号[${this.index}] 查询失败:${message}🚫`);
if (isTokenError(message)) this.removeCachedToken();
}
}
}
!(async () => {
await getNotice();
$.checkEnv(ckName);
for (const user of $.userList) {
await new Task(user).run();
}
})()
.catch((e) => console.log(e))
.finally(() => $.done());
async function getNotice() {
try {
const { data: res } = await axios.request({
url: "https://ghproxy.net/https://raw.githubusercontent.com/smallfawn/Note/refs/heads/main/Notice.json",
headers: { "User-Agent": defaultUserAgent },
timeout: 3000,
});
$.log(res);
return res;
} catch (e) {}
}
-478
View File
@@ -1,478 +0,0 @@
/*
------------------------------------------
@Author: sm
@Date: 2026.06.01
@Description: 海天美味馆小程序
cron: 30 11 * * *
------------------------------------------
#Notice:
变量名 haitian
变量值:wx_server 里的 openid/账号标识,多账号&或换行
需要配置:wx_server_url、wx_auth
⚠️【免责声明】
------------------------------------------
1、此脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。
2、由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。
3、请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。
4、此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。
5、本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。
6、如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此脚本。
7、所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此免责声明。
*/
const { Env } = require("../tools/env.js");
const $ = new Env("海天美味馆小程序");
const axios = require("axios");
const fs = require("fs");
const path = require("path");
const ckName = "haitian";
const strSplitor = "#";
const MINI_APP_ID = "wx7a890ea13f50d7b6";
const PAGE_VERSION = "772";
const API_BASE = "https://cmallapi.xkmm.cn/buyer-api";
const BASE_API = "https://cmallapi.xkmm.cn/base-api";
const TOKEN_CACHE_FILE = path.join(__dirname, "haitian_token_cache.json");
const defaultUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_7_15 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.70(0x1800462d) NetType/WIFI Language/zh_CN";
function readTokenCache() {
try {
if (!fs.existsSync(TOKEN_CACHE_FILE)) return {};
return JSON.parse(fs.readFileSync(TOKEN_CACHE_FILE, "utf8")) || {};
} catch (e) {
return {};
}
}
function writeTokenCache(cache) {
try {
fs.writeFileSync(TOKEN_CACHE_FILE, JSON.stringify(cache, null, 2), "utf8");
} catch (e) {
$.log(`写入token缓存失败: ${e.message || e}`);
}
}
function shortToken(token = "") {
const value = String(token || "");
return value ? `${value.slice(0, 6)}***${value.slice(-6)}` : "";
}
function maskPhone(phone = "") {
return String(phone || "").replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2");
}
function randomUuid(len = 20) {
const chars = "abcdef0123456789";
let value = "";
for (let i = 0; i < len; i++) value += chars[Math.floor(Math.random() * chars.length)];
return value;
}
function isTokenError(message) {
return /401|403|token|登录|授权|失效|过期|no-show-toast/i.test(String(message || ""));
}
class Task {
constructor(env) {
this.index = $.userIdx++;
this.raw = String(env || "").trim();
this.user = this.raw.split(strSplitor);
this.accountId = this.user[0].trim();
this.token = "";
this.refreshToken = "";
this.communityToken = "";
this.uuid = randomUuid();
this.activity_code = "";
this.userFlag = false;
this.userInfo = {};
this.isLegacyToken = this.user.length >= 2;
if (this.isLegacyToken) {
this.token = this.user[0].trim();
this.uuid = this.user[1].trim() || this.uuid;
this.accountId = `legacy:${shortToken(this.token)}`;
}
}
async run() {
if (this.isLegacyToken) {
$.log(`账号[${this.index}] 检测到旧版 authorization#uuid 格式,先迁移到缓存`);
this.saveCachedToken();
} else {
const cached = this.getCachedToken();
if (cached?.accessToken) {
this.applyToken(cached);
$.log(`账号[${this.index}] 使用缓存token: ${shortToken(this.token)}`);
if (!(await this.checkToken())) {
this.removeCachedToken();
$.log(`账号[${this.index}] 缓存token失效,重新CODE登录`);
}
}
if (!this.token) {
await this.loginByWxCode();
if (!this.token) return;
}
}
await this.getUserInfo();
if (!this.userFlag) return;
await this.task_1();
await this.task_2();
await this.getSignActivity();
if (this.activity_code) {
await this.signIn();
await this.getLotteryTaskList();
await this.getLotteryNum();
}
}
cacheKey() {
return this.isLegacyToken ? this.accountId : this.raw;
}
getCachedToken() {
const cache = readTokenCache();
return cache[this.cacheKey()] || null;
}
saveCachedToken() {
if (!this.token) return;
const cache = readTokenCache();
cache[this.cacheKey()] = {
accessToken: this.token,
refreshToken: this.refreshToken,
communityToken: this.communityToken,
uuid: this.uuid,
userInfo: this.userInfo,
updatedAt: new Date().toISOString(),
};
writeTokenCache(cache);
}
removeCachedToken() {
const cache = readTokenCache();
if (cache[this.cacheKey()]) {
delete cache[this.cacheKey()];
writeTokenCache(cache);
}
this.token = "";
this.refreshToken = "";
this.communityToken = "";
this.userInfo = {};
}
applyToken(data = {}) {
this.token = data.accessToken || data.token || "";
this.refreshToken = data.refreshToken || "";
this.communityToken = data.communityToken || "";
this.uuid = data.uuid || this.uuid;
this.userInfo = data.userInfo || {};
}
headers(extra = {}, auth = true, isJson = false) {
const headers = {
"Connection": "keep-alive",
"envVersion": "release",
"Content-Type": isJson ? "application/json" : "application/x-www-form-urlencoded",
"uuid": this.uuid,
"Accept-Encoding": "gzip,compress,br,deflate",
"User-Agent": defaultUserAgent,
"Referer": `https://servicewechat.com/${MINI_APP_ID}/${PAGE_VERSION}/page-frame.html`,
...extra,
};
if (auth) {
headers.Authorization = this.token;
if (this.communityToken) headers["X-Haday-Token"] = this.communityToken;
}
return headers;
}
async request(pathname, { method = "GET", params, data, auth = true, isJson = false, base = API_BASE } = {}) {
const options = {
method,
url: `${base}${pathname}`,
params,
headers: this.headers({}, auth, isJson),
timeout: 20000,
validateStatus: () => true,
};
if (data !== undefined) options.data = isJson ? data : new URLSearchParams(data).toString();
const { data: result, status } = await axios.request(options);
if (status === 401 || status === 403) throw new Error(`HTTP ${status}: ${result?.message || JSON.stringify(result)}`);
if (status < 200 || status >= 300) throw new Error(`HTTP ${status}: ${JSON.stringify(result)}`);
if (result && typeof result === "object" && result.code && !["200", "0"].includes(String(result.code))) {
throw new Error(`${result.code} ${result.message || result.msg || JSON.stringify(result)}`.trim());
}
return result;
}
async getOperateData() {
if (!process.env.wx_auth) throw new Error("缺少 wx_auth,无法从 wx_server 获取登录数据");
const url = (process.env.wx_server_url || "http://192.168.31.196:8787").replace(/\/$/, "");
const { data } = await axios.post(`${url}/wx/operatedata`, {
appid: MINI_APP_ID,
openid: this.accountId,
}, {
headers: { auth: process.env.wx_auth },
timeout: 45000,
validateStatus: () => true,
});
const result = data?.data || {};
if (!data?.status || !result.code || !result.encryptedData || !result.iv) {
throw new Error(`wx_server 未返回完整登录数据: ${JSON.stringify(data)}`);
}
return result;
}
async getArticleIds() {
try {
const result = await this.request("/pages/REGISTRATION_AGREEMENT,PRIVACY_PROTECTION_CLAUSE/articles/list", {
base: BASE_API,
auth: false,
});
const ids = Array.isArray(result) ? result.map(item => item.article_id).filter(Boolean) : [];
return ids.join(",");
} catch (e) {
$.log(`账号[${this.index}] 获取协议ID失败,继续登录: ${e.message || e}`);
return "";
}
}
async loginByWxCode() {
try {
const wxData = await this.getOperateData();
const articleIds = await this.getArticleIds();
const params = {
edata: wxData.encryptedData,
iv: wxData.iv,
code: wxData.code,
uuid: this.uuid,
article_ids: articleIds,
app_versions: "1.0.0",
};
const result = await this.request("/wechat/mini/phoneNew/login", {
method: "POST",
params,
auth: false,
});
const token = result?.access_token || result?.accessToken || "";
if (!result?.success || !token) throw new Error(`登录响应未返回token: ${JSON.stringify(result)}`);
this.token = token;
this.refreshToken = result.refresh_token || result.refreshToken || "";
await this.loginCommunityToken().catch(() => {});
this.saveCachedToken();
$.log(`账号[${this.index}] CODE登录成功: ${shortToken(this.token)}`);
} catch (e) {
$.log(`账号[${this.index}] CODE登录失败: ${e.message || e}`);
}
}
async loginCommunityToken() {
if (!this.token) return;
const result = await this.request("/wx/auth/loginByToken", {
method: "POST",
base: "https://cmallwap.xkmm.cn/haday",
data: { access_token: this.token },
auth: false,
isJson: true,
});
this.communityToken = result?.data || result || "";
this.saveCachedToken();
}
async checkToken() {
try {
await this.request("/members");
return true;
} catch (e) {
return false;
}
}
async getUserInfo() {
try {
const result = await this.request("/members");
if (result?.member_id) {
this.userInfo = result;
this.saveCachedToken();
$.log(`🌸账号[${this.index}] [${result.member_id}][${maskPhone(result.mobile)}] 当前积分[${result.consum_point}]🎉`);
this.userFlag = true;
} else {
$.log(`🌸账号[${this.index}] 查询-失败:${JSON.stringify(result)}❌`);
}
} catch (e) {
const message = e.message || e;
$.log(`🌸账号[${this.index}] 查询-失败:${message}❌`);
if (isTokenError(message)) this.removeCachedToken();
}
}
async getSignActivity() {
try {
const result = await this.request("/sign/activity/code", {
params: { activityCode: "" },
});
if (result?.activity_code) {
$.log(`获取活动信息成功,活动ID:${result.activity_code}`);
this.activity_code = result.activity_code;
} else {
$.log(`获取活动信息失败:${JSON.stringify(result)}❌`);
}
} catch (e) {
$.log(`获取活动信息失败:${e.message || e}❌`);
}
}
async signIn() {
try {
const result = await this.request("/sign/activity/sign", {
method: "POST",
isJson: true,
data: { activity_code: this.activity_code, fill_date: "" },
});
if (result?.is_sign === true || result?.success === true) {
$.log("签到成功");
} else {
$.log(`签到失败 [${result?.message || JSON.stringify(result)}]`);
}
} catch (e) {
const message = String(e.message || e);
if (/已签|重复/.test(message)) {
$.log("今日已签到");
return;
}
$.log(`签到失败 [${message}]`);
}
}
async task_1() {
try {
const result = await this.request("/members/browsePage", {
method: "POST",
data: {},
});
if (result?.code == 200 || result?.statusCode == 200 || result === "") {
$.log("浏览界面 操作成功");
} else {
$.log(`浏览界面 操作失败[${JSON.stringify(result)}]`);
}
} catch (e) {
$.log(`浏览界面 操作失败[${e.message || e}]`);
}
}
async task_2() {
try {
const result = await this.request("/members/commnity/brosing/duration/add", {
method: "POST",
params: { seconds: 10 },
data: {},
});
if (result?.statusCode == 200 || result?.code == 200 || result === "") {
$.log("浏览社区 操作成功");
} else {
$.log(`浏览社区 操作失败 貌似是正常的 [${JSON.stringify(result)}]`);
}
} catch (e) {
$.log(`浏览社区 操作失败 貌似是正常的 [${e.message || e}]`);
}
}
async doLottery() {
try {
const result = await this.request("/lucky/activity/extract", {
params: { activityCode: `jfcj${this.activity_code}` },
});
if (result?.lucky_record_vo) {
$.log(`🌸账号[${this.index}]🕊抽奖结果:${result.lucky_record_vo.prize_name}🎉`);
} else {
$.log(`🌸账号[${this.index}] 抽奖-失败:${JSON.stringify(result)}❌`);
}
} catch (e) {
$.log(`🌸账号[${this.index}] 抽奖-失败:${e.message || e}❌`);
}
}
async getLotteryTaskList() {
try {
const result = await this.request(`/lucky/task/package/jfcj${this.activity_code}`);
if (result?.member_id) {
$.log(`🌸账号[${this.index}]🕊抽奖次数任务🎉`);
for (const task of result.task_list || []) {
if (task.today_available_task_number >= 1 && task.today_obtained_task_number < task.today_available_task_number) {
if (task.task_key === "LOGIN") {
$.log(`🌸账号[${this.index}]🕊正在完成任务:${task.task_name}🎉`);
const res = await this.request(`/lucky/task/getLoginOpporturnity/jfcj${this.activity_code}`, {
method: "PUT",
data: {},
});
$.log(JSON.stringify(res));
}
if (task.task_key === "BROWSE_PAGE_TASK") {
$.log(`🌸账号[${this.index}]🕊正在完成任务:${task.task_name}🎉`);
await this.lotteryTaskBrowser(task.link);
}
}
}
} else {
$.log(`🌸账号[${this.index}] 抽奖次数任务-失败:${JSON.stringify(result)}❌`);
}
} catch (e) {
$.log(`🌸账号[${this.index}] 抽奖次数任务-失败:${e.message || e}❌`);
}
}
async lotteryTaskBrowser(link) {
await this.request(`/lucky/task/browse/page/start/jfcj${this.activity_code}`, {
params: { pageUrl: link },
}).catch(() => {});
await $.wait(20 * 1000);
await this.request(`/lucky/task/browse/page/end/jfcj${this.activity_code}`, {
params: { pageUrl: link },
}).catch(() => {});
}
async getLotteryNum() {
try {
const result = await this.request("/lucky/activity/opporturnity", {
params: { activityCode: `jfcj${this.activity_code}` },
});
if (result?.can_use > 0) {
$.log(`当前剩余抽奖次数:${result.can_use}`);
for (let can = 0; can < result.can_use; can++) {
await this.doLottery();
}
} else {
$.log("当前剩余抽奖次数0");
}
} catch (e) {
$.log(`查询抽奖次数失败:${e.message || e}`);
}
}
}
!(async () => {
await getNotice();
$.checkEnv(ckName);
for (const user of $.userList) {
await new Task(user).run();
}
})()
.catch((e) => console.log(e))
.finally(() => $.done());
async function getNotice() {
try {
const { data: res } = await axios.request({
url: "https://ghproxy.net/https://raw.githubusercontent.com/smallfawn/Note/refs/heads/main/Notice.json",
headers: { "User-Agent": defaultUserAgent },
timeout: 3000,
});
$.log(res);
return res;
} catch (e) {}
}
-242
View File
@@ -1,242 +0,0 @@
/*
------------------------------------------
@Author: sm
@Date: 2024.06.07 19:15
@Description: 海尔智家
cron: 30 7 * * *
------------------------------------------
#Notice:
变量名hezj
变量值填写 wx_server 里的 openid/账号标识,多账户&或换行
需要配置 wx_server_url、wx_auth
⚠️【免责声明】
------------------------------------------
1、此脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。
2、由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。
3、请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。
4、此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。
5、本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。
6、如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此脚本。
7、所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。
*/
const { Env } = require("../tools/env")
const $ = new Env("海尔智家");
let ckName = `hezj`;
const strSplitor = "#";
const axios = require("axios");
const crypto = require("crypto");
const WeChatCodeServer = require("../wxapp/wcs");
const defaultUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.31(0x18001e31) NetType/WIFI Language/zh_CN miniProgram"
const MINI_APP_ID = "wxe24b2f1f4e378891";
const PAGE_VERSION = "475";
const HA_APP_ID = "MB-SHEZJAPPWXXCX-0000";
const HA_APP_KEY = "79ce99cc7f9804663939676031b8a427";
const API_HOST = "https://zj.haier.net";
const wechat = new WeChatCodeServer({
url: process.env.wx_server_url || "http://192.168.31.196:8787",
appid: MINI_APP_ID,
auth: process.env.wx_auth || "",
});
function sign256(path, body, timestamp) {
const bodyStr = body ? JSON.stringify(body) : "";
return crypto.createHash("sha256").update(path + bodyStr + HA_APP_ID + HA_APP_KEY + timestamp).digest("hex");
}
function maskToken(token = "") {
if (!token) return "";
return token.length > 14 ? `${token.slice(0, 6)}...${token.slice(-6)}` : token;
}
class Task {
constructor(env) {
this.index = $.userIdx++
this.user = env.split(strSplitor);
this.openid = this.user[0].trim();
this.token = "";
this.clientId = `${Date.now()}${$.randomString(12)}`;
}
request(options) {
const path = options.path || new URL(options.url).pathname;
const timestamp = Date.now();
let baseHeaers = {
"host": "zj.haier.net",
"Content-Type": "application/json;charset=UTF-8",
"appId": HA_APP_ID,
"appKey": HA_APP_KEY,
"timestamp": timestamp,
"platForm": "sc-mp-wx-zjapp",
"ENV": "",
"accessToken": options.isHeaderDelToken ? "" : this.token,
"accountToken": options.isHeaderDelToken ? "" : this.token,
"ak": options.isHeaderDelToken ? "" : this.token,
"clientId": this.clientId,
"accept": "*/*",
"accept-language": "zh-CN,zh-Hans;q=0.9",
"user-agent": defaultUserAgent,
"referer": `https://servicewechat.com/${MINI_APP_ID}/${PAGE_VERSION}/page-frame.html`,
}
if (options.isSign256) {
baseHeaers.sign = sign256(path, options.data, timestamp);
}
options.headers = Object.assign(baseHeaers, options.headers || {});
delete options.path;
delete options.isSign256;
delete options.isHeaderDelToken;
return axios.request(options)
}
async run() {
await this.loginByWxCode();
if (!this.token) return;
await this.pointInfo()
await this.signIn()
}
async getLoginCode() {
if (!process.env.wx_auth) throw new Error("缺少 wx_auth,无法从 wx_server 获取 code");
const { data } = await wechat.getCode(this.openid);
const code = data?.code || data?.data?.code;
if (!code) throw new Error(`wx_server 未返回 code: ${JSON.stringify(data)}`);
return code;
}
async loginByWxCode() {
try {
const code = await this.getLoginCode();
const tokenInfo = await this.jscode2session(code);
const accountToken = tokenInfo?.accountToken;
if (!accountToken) throw new Error(`登录响应未返回 accountToken: ${JSON.stringify(tokenInfo)}`);
await this.queryUserInfo(accountToken);
this.token = accountToken;
$.log(`账号[${this.index}] CODE登录成功: ${maskToken(this.token)}`);
} catch (e) {
$.log(`账号[${this.index}] CODE登录失败: ${e.message || e}`);
}
}
async jscode2session(code) {
const path = "/api-gw/oauthserver/applet/v1/jscode2session";
let options = {
method: "POST",
url: API_HOST + path,
path,
isSign256: true,
isHeaderDelToken: true,
data: { code },
};
const { data: result } = await this.request(options);
if (result?.retCode !== "00000" && result?.code !== 200 && !result?.success) {
throw new Error(result?.retInfo || result?.message || JSON.stringify(result));
}
return result?.data?.tokenInfo || result?.data || {};
}
async queryUserInfo(accountToken) {
const path = "/api-gw/oauthserver/applet/v1/userinfo/query";
let options = {
method: "POST",
url: API_HOST + path,
path,
isSign256: true,
isHeaderDelToken: true,
data: { accountToken },
};
const { data: result } = await this.request(options);
if (result?.retCode !== "00000" && result?.code !== 200 && !result?.success) {
throw new Error(result?.retInfo || result?.message || JSON.stringify(result));
}
const info = result?.data?.userinfo || {};
const name = info.nickName || info.nickname || info.userName || "未知用户";
const phone = info.mobile || info.phoneNumber || "";
$.log(`账号[${this.index}] 用户: ${name}${phone ? ` ${phone.slice(0, 3)}****${phone.slice(-4)}` : ""}`);
return info;
}
async pointInfo() {
const path = "/zjapi/zjBaseServer/signDetail/getUserPointsAndWallet";
let options = {
method: 'POST',
url: API_HOST + path,
path,
headers: {},
data: {
}
};
let { data: result } = await this.request(options);
if (result.retCode == '00000') {
$.log(`海贝:${result.data.haiBeiTotal}`)
$.log(`红包:${result.data.wallet}`)
} else {
$.log(`查询余额失败: ${result.retInfo}`)
}
}
async signIn() {
const path = "/api-gw/zjBaseServer/daily/sign";
let options = {
method: 'POST',
url: API_HOST + path,
path,
headers: {},
data: {
}
};
let { data: result } = await this.request(options);
if (result?.retCode == '00000') {
//打印签到结果
$.log(`🌸账号[${this.index}]` + `🕊当前已签到${result.data.totalSignDay}天🎉`);
} else {
$.log(`🌸账号[${this.index}] 签到-失败:${result.retInfo}❌`)
}
}
}
!(async () => {
await getNotice()
$.checkEnv(ckName);
for (let user of $.userList) {
await new Task(user).run();
}
})()
.catch((e) => console.log(e))
.finally(() => $.done());
async function getNotice() {
try {
let options = {
url: `https://ghproxy.net/https://raw.githubusercontent.com/smallfawn/Note/refs/heads/main/Notice.json`,
headers: {
"User-Agent": defaultUserAgent,
},
timeout:3000
}
let {
data: res
} = await axios.request(options);
$.log(res)
return res
} catch (e) {}
}
-399
View File
@@ -1,399 +0,0 @@
/*
------------------------------------------
@Author: sm
@Date: 2026.06.01
@Description: IQOO社区小程序 积分脚本
cron: 30 8 * * *
------------------------------------------
#Notice:
变量名 iqoo
变量值:wx_server 里的 openid/账号标识,多账户&或换行
需要配置:wx_server_url、wx_auth
⚠️【免责声明】
------------------------------------------
1、此脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。
2、由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。
3、请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。
4、此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。
5、本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。
6、如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此脚本。
7、所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。
*/
const { Env } = require("../tools/env.js");
const $ = new Env("iqoo社区");
const axios = require("axios");
const crypto = require("crypto");
const fs = require("fs");
const path = require("path");
const ckName = "iqoo";
const strSplitor = "#";
const MINI_APP_ID = "wxcf4266fbc9463132";
const PAGE_VERSION = "253";
const API_BASE = "https://bbs-api.iqoo.com/api/";
const TOKEN_CACHE_FILE = path.join(__dirname, "iqoo_token_cache.json");
const SIGN_SECRET = "2618194b0ebb620055e19cf9811d3c13";
const USER_AGENT = "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 WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf254173b) XWEB/19027";
const defaultUserAgent = USER_AGENT;
function readTokenCache() {
try {
if (!fs.existsSync(TOKEN_CACHE_FILE)) return {};
return JSON.parse(fs.readFileSync(TOKEN_CACHE_FILE, "utf8")) || {};
} catch (e) {
return {};
}
}
function writeTokenCache(cache) {
try {
fs.writeFileSync(TOKEN_CACHE_FILE, JSON.stringify(cache, null, 2), "utf8");
} catch (e) {
$.log(`写入token缓存失败: ${e.message || e}`);
}
}
function shortToken(token = "") {
const value = String(token || "").replace(/^Bearer\s+/i, "");
return value ? `${value.slice(0, 6)}***${value.slice(-6)}` : "";
}
function isSuccess(result) {
return Number(result?.Code) === 0;
}
function isTokenError(message) {
return /-4011|401|403|token|登录|授权|请先登录|跳转到登录页|访客没有权限/i.test(String(message || ""));
}
function maskName(value = "") {
const text = String(value || "");
return text.length > 10 ? `${text.slice(0, 4)}***${text.slice(-3)}` : text;
}
class Task {
constructor(env) {
this.index = $.userIdx++;
this.user = String(env || "").trim().split(strSplitor);
this.openid = this.user[0].trim();
this.token = "";
this.refreshToken = "";
this.userId = "";
this.userInfo = {};
this.postId = "";
this.threadId = "";
this.visitor = crypto.createHash("md5").update(this.openid || String(Date.now())).digest("hex");
}
async run() {
const cached = this.getCachedToken();
if (cached?.accessToken) {
this.applyToken(cached);
$.log(`账号[${this.index}] 使用缓存token: ${shortToken(this.token)}`);
if (!(await this.checkToken())) {
this.removeCachedToken();
$.log(`账号[${this.index}] 缓存token失效,重新登录`);
}
}
if (!this.token) {
await this.loginByWxCode();
if (!this.token) return;
}
await this.userInfoRequest();
await this.getTreadList();
if (this.threadId && this.postId) {
await this.likePost(this.threadId, this.postId);
await $.wait(5000);
await this.sharePost(this.threadId);
await $.wait(5000);
await this.viewPost(this.threadId);
await $.wait(5000);
await this.commonPost(this.threadId);
}
await this.getDrawNum();
await this.signIn();
}
getCachedToken() {
const cache = readTokenCache();
return cache[this.openid] || null;
}
saveCachedToken() {
if (!this.token) return;
const cache = readTokenCache();
cache[this.openid] = {
accessToken: this.token,
refreshToken: this.refreshToken,
userId: this.userId,
userInfo: this.userInfo,
visitor: this.visitor,
updatedAt: new Date().toISOString(),
};
writeTokenCache(cache);
}
removeCachedToken() {
const cache = readTokenCache();
if (cache[this.openid]) {
delete cache[this.openid];
writeTokenCache(cache);
}
this.token = "";
this.refreshToken = "";
this.userId = "";
this.userInfo = {};
}
applyToken(data = {}) {
this.token = data.accessToken || data.token || "";
this.refreshToken = data.refreshToken || "";
this.userId = data.userId || data.uid || data.userInfo?.userId || data.userInfo?.uid || "";
this.userInfo = data.userInfo || data.user || {};
this.visitor = data.visitor || this.visitor;
}
queryString(data = {}) {
const list = [];
for (const [key, value] of Object.entries(data || {})) {
if (value === undefined || value === null) continue;
list.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
}
return list.join("&");
}
getSign(method, apiPath, data = {}) {
const upperMethod = method.toUpperCase();
const timestamp = Math.floor(Date.now() / 1000);
const query = upperMethod === "GET" ? this.queryString(data) : "";
const body = upperMethod === "GET" ? "" : JSON.stringify(data || {});
const raw = `${upperMethod}&/api/${apiPath}&${query}&${body}&appid=1002&timestamp=${timestamp}`;
const signature = crypto.createHmac("sha256", SIGN_SECRET).update(raw).digest("base64");
return `IQOO-HMAC-SHA256 appid=1002,timestamp=${timestamp},signature=${signature}`;
}
headers(method, apiPath, data, extra = {}) {
return {
Authorization: this.token ? `Bearer ${this.token}` : "Bearer ",
"X-Visitor": this.visitor,
"X-Platform": "mini",
SIGN: this.getSign(method, apiPath, data),
"Content-Nonce": "",
"User-Agent": USER_AGENT,
Referer: `https://servicewechat.com/${MINI_APP_ID}/${PAGE_VERSION}/page-frame.html`,
...extra,
};
}
async request(apiPath, data = {}, options = {}) {
const method = (options.method || "POST").toUpperCase();
const req = {
method,
url: new URL(apiPath, API_BASE).toString(),
headers: this.headers(method, apiPath, data, options.headers || {}),
timeout: 20000,
validateStatus: () => true,
};
if (method === "GET") req.params = data;
else req.data = data || {};
const { data: result, status } = await axios.request(req);
if (status !== 200) throw new Error(`HTTP ${status}: ${JSON.stringify(result)}`);
if (!isSuccess(result)) throw new Error(`${result?.Code ?? ""} ${result?.Message || JSON.stringify(result)}`.trim());
return result;
}
async getOperateData() {
if (!process.env.wx_auth) throw new Error("缺少 wx_auth,无法从 wx_server 获取登录数据");
const url = (process.env.wx_server_url || "http://192.168.31.196:8787").replace(/\/$/, "");
const { data } = await axios.post(`${url}/wx/operatedata`, {
appid: MINI_APP_ID,
openid: this.openid,
}, {
headers: { auth: process.env.wx_auth },
timeout: 45000,
});
const result = data?.data || {};
if (!data?.status || !result.code || !result.encryptedData || !result.iv) {
throw new Error(`wx_server 未返回完整登录数据: ${JSON.stringify(data)}`);
}
return result;
}
async loginByWxCode() {
try {
const wxData = await this.getOperateData();
const payload = {
code: wxData.code,
encryptedData: wxData.encryptedData,
iv: wxData.iv,
from: 46,
};
const result = await this.request("v3/users/vivo/mini", payload);
const data = result?.Data || {};
const token = data.accessToken || data.token || "";
if (!token) throw new Error(`登录响应未返回token: ${JSON.stringify(result)}`);
this.token = token;
this.refreshToken = data.refreshToken || "";
this.userId = data.userId || data.uid || "";
this.userInfo = data.user || data;
this.saveCachedToken();
$.log(`账号[${this.index}] CODE登录成功: ${maskName(this.userInfo?.nickname || this.userInfo?.username || this.userId)}`);
} catch (e) {
$.log(`账号[${this.index}] CODE登录失败: ${e.message || e}`);
}
}
async checkToken() {
try {
if (!this.userId) return false;
await this.request("v3/user", { userId: this.userId }, { method: "GET" });
return true;
} catch (e) {
return false;
}
}
async userInfoRequest() {
if (!this.userId) return;
try {
const result = await this.request("v3/user", { userId: this.userId }, { method: "GET" });
this.userInfo = result?.Data || this.userInfo;
this.saveCachedToken();
const name = this.userInfo?.nickname || this.userInfo?.username || this.userInfo?.userName || this.userId;
const score = this.userInfo?.score ?? this.userInfo?.points ?? this.userInfo?.coolCoin ?? "未知";
$.log(`账号[${this.index}] 用户:${maskName(name)} 酷币:${score}`);
} catch (e) {
const message = String(e.message || e);
$.log(`账号[${this.index}] 获取用户信息失败:${message}`);
if (isTokenError(message)) this.removeCachedToken();
}
}
async getDrawNum() {
try {
const result = await this.request("v3/today.draw.count", {}, { method: "GET" });
if (result?.Data?.count === 0) {
await this.draw();
} else {
$.log(`账号[${this.index}] 今日已抽奖`);
}
} catch (e) {
$.log(`账号[${this.index}] 查询抽奖次数失败:${e.message || e}`);
}
}
async draw() {
try {
const result = await this.request("v3/luck.draw", {});
$.log(`账号[${this.index}] 抽奖成功 获得${result?.Data?.prize_name || "奖励"}`);
} catch (e) {
$.log(`账号[${this.index}] 抽奖失败:${e.message || e}`);
}
}
async signIn() {
try {
const result = await this.request("v3/sign", { from: "group" });
$.log(`🌸账号[${this.index}]🕊当前已签到${result.Data.serialDays}天🎉 获得积分${result.Data.score} 当前积分${result.Data.scoreCount}`);
} catch (e) {
const message = String(e.message || e);
if (/已签|重复|今日|13006/.test(message)) {
$.log(`🌸账号[${this.index}] 今日已签到`);
return;
}
$.log(`🌸账号[${this.index}] 签到失败:${message}❌`);
if (isTokenError(message)) this.removeCachedToken();
}
}
async likePost(threadId, postId) {
try {
await this.request("v3/posts.update", { id: threadId, postId, data: { attributes: { isLiked: true } } });
$.log(`账号[${this.index}] 帖子点赞成功`);
} catch (e) {
$.log(`账号[${this.index}] 帖子点赞失败:${e.message || e}❌`);
}
try {
await this.request("v3/posts.update", { id: threadId, postId, data: { attributes: { isLiked: false } } });
$.log(`账号[${this.index}] 帖子取消点赞成功`);
} catch (e) {
$.log(`账号[${this.index}] 帖子取消点赞失败:${e.message || e}❌`);
}
}
async sharePost(threadId) {
try {
await this.request("v3/thread.share", { threadId });
$.log(`账号[${this.index}] 帖子分享成功`);
} catch (e) {
$.log(`账号[${this.index}] 帖子分享失败:${e.message || e}❌`);
}
}
async viewPost(threadId) {
try {
await this.request("v3/view.count", { threadId, type: 0 }, { method: "GET" });
$.log(`账号[${this.index}] 帖子浏览成功`);
} catch (e) {
$.log(`账号[${this.index}] 帖子浏览失败:${e.message || e}❌`);
}
}
async commonPost(threadId) {
try {
await this.request("v3/posts.create", { id: threadId, type: 0, content: "666", source: "", attachments: [] });
$.log(`账号[${this.index}] 帖子评论成功`);
} catch (e) {
$.log(`账号[${this.index}] 帖子评论失败:${e.message || e}❌`);
}
}
async getTreadList() {
try {
const result = await this.request("v3/thread.list", {
scope: 5,
page: 1,
perPage: 10,
"filter[sort]": 4,
"filter[essence]": 1,
sequence: 0,
}, { method: "GET" });
const first = result?.Data?.pageData?.[0] || {};
this.threadId = first.threadId || "";
this.postId = first.postId || "";
} catch (e) {
$.log(`账号[${this.index}] 获取帖子列表失败:${e.message || e}`);
}
}
}
!(async () => {
await getNotice();
$.checkEnv(ckName);
for (const user of $.userList) {
await new Task(user).run();
}
})()
.catch((e) => console.log(e))
.finally(() => $.done());
async function getNotice() {
try {
const options = {
url: "https://ghproxy.net/https://raw.githubusercontent.com/smallfawn/Note/refs/heads/main/Notice.json",
headers: {
"User-Agent": defaultUserAgent,
},
timeout: 3000,
};
const { data: res } = await axios.request(options);
$.log(res);
return res;
} catch (e) {}
}
-296
View File
@@ -1,296 +0,0 @@
/*
------------------------------------------
@Author: sm
@Date: 2026.06.01
@Description: 康师傅畅饮社
cron: 30 10 * * *
------------------------------------------
#Notice:
康师傅畅饮社小程序
变量名 ksfcys
变量值:wx_server 里的 openid/账号标识,多账户&或换行
需要配置:wx_server_url、wx_auth
⚠️【免责声明】
------------------------------------------
1、此脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。
2、由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。
3、请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。
4、此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。
5、本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。
6、如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此脚本。
7、所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。
*/
const { Env } = require("../tools/env.js");
const $ = new Env("康师傅畅饮社");
const axios = require("axios");
const fs = require("fs");
const path = require("path");
const WeChatServer = require("../wxapp/wcs.js");
const ckName = "ksfcys";
const MINI_APP_ID = "wx54f3e6a00f7973a7";
const API_BASE = "https://club.biqr.cn/";
const FORUM_BASE = "https://nclub.gdshcm.com/pro/";
const TOKEN_CACHE_FILE = path.join(__dirname, "kangshifu_token_cache.json");
const USER_AGENT = "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 WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf254173b) XWEB/19027";
const defaultUserAgent = USER_AGENT;
const wechat = new WeChatServer({
url: process.env.wx_server_url || "http://192.168.31.196:8787",
appid: MINI_APP_ID,
auth: process.env.wx_auth || "",
});
function readTokenCache() {
try {
if (!fs.existsSync(TOKEN_CACHE_FILE)) return {};
return JSON.parse(fs.readFileSync(TOKEN_CACHE_FILE, "utf8")) || {};
} catch (e) {
return {};
}
}
function writeTokenCache(cache) {
try {
fs.writeFileSync(TOKEN_CACHE_FILE, JSON.stringify(cache, null, 2), "utf8");
} catch (e) {
$.log(`写入token缓存失败: ${e.message || e}`);
}
}
function shortToken(token = "") {
const value = String(token);
return value ? `${value.slice(0, 6)}***${value.slice(-6)}` : "";
}
function maskPhone(phone = "") {
return String(phone).replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2");
}
function isSuccess(result) {
return Number(result?.code) === 0;
}
function isTokenError(message) {
return /(^|[^0-9])600([^0-9]|$)|4002|token|登录|授权|未登录|无效|过期/i.test(String(message || ""));
}
function formBody(data = {}) {
return new URLSearchParams(data).toString();
}
class Task {
constructor(openid) {
this.index = $.userIdx++;
this.openid = String(openid || "").trim();
this.token = "";
this.member = {};
}
async run() {
const cached = this.getCachedToken();
if (cached?.token) {
this.token = cached.token;
this.member = cached.member || {};
$.log(`账号[${this.index}] 使用缓存token: ${shortToken(this.token)}`);
if (!(await this.checkToken())) {
this.removeCachedToken();
$.log(`账号[${this.index}] 缓存token失效,重新登录`);
}
}
if (!this.token) {
await this.loginByWxCode();
if (!this.token) return;
}
await this.memberInfo();
await this.signIn();
await this.memberInfo("签到后");
}
getCachedToken() {
const cache = readTokenCache();
return cache[this.openid] || null;
}
saveCachedToken() {
if (!this.token) return;
const cache = readTokenCache();
cache[this.openid] = {
token: this.token,
member: this.member,
updatedAt: new Date().toISOString(),
};
writeTokenCache(cache);
}
removeCachedToken() {
const cache = readTokenCache();
if (cache[this.openid]) {
delete cache[this.openid];
writeTokenCache(cache);
}
this.token = "";
this.member = {};
}
headers(extra = {}) {
const headers = {
"User-Agent": USER_AGENT,
"Accept": "application/json, text/plain, */*",
"xweb_xhr": "1",
"Referer": "https://servicewechat.com/wx54f3e6a00f7973a7/816/page-frame.html",
"Accept-Language": "zh-CN,zh;q=0.9",
...extra,
};
if (this.token) headers.token = this.token;
return headers;
}
async request(baseURL, apiPath, { method = "GET", data, params, auth = true, form = false } = {}) {
const options = {
method,
url: new URL(apiPath, baseURL).toString(),
params,
headers: this.headers(form ? {
"Content-Type": "application/x-www-form-urlencoded;",
} : {}),
timeout: 15000,
validateStatus: () => true,
};
if (!auth) delete options.headers.token;
if (data !== undefined) options.data = form ? formBody(data) : data;
const { data: result, status } = await axios.request(options);
if (status !== 200) throw new Error(`HTTP ${status}: ${JSON.stringify(result)}`);
if (!isSuccess(result)) throw new Error(`${result?.code ?? ""} ${result?.msg || result?.error || JSON.stringify(result)}`.trim());
return result;
}
async getLoginCode() {
if (!process.env.wx_auth) throw new Error("缺少 wx_auth,无法从 wx_server 获取 code");
const { data } = await wechat.getCode(this.openid);
const code = data?.code || data?.data?.code;
if (!code) throw new Error(`wx_server 未返回 code: ${JSON.stringify(data)}`);
return code;
}
async loginByWxCode() {
try {
const code = await this.getLoginCode();
const result = await this.request(FORUM_BASE, "whale-member/api/login/login", {
method: "POST",
auth: false,
data: {
code,
inviterId: "",
inviterType: "",
inviterMatchUserId: "",
spUrl: "",
},
});
const token = result?.data?.token || "";
if (!token) throw new Error(`登录响应未返回token: ${JSON.stringify(result)}`);
this.token = token;
this.member = result?.data?.member || {};
this.saveCachedToken();
$.log(`账号[${this.index}] 登录成功: ${this.member.nickname || maskPhone(this.member.phone) || this.member.id || ""}`);
} catch (e) {
$.log(`账号[${this.index}] 登录失败: ${e.message || e}`);
}
}
async checkToken() {
try {
await this.getMemberInfo();
return true;
} catch (e) {
return false;
}
}
async getMemberInfo() {
const result = await this.request(FORUM_BASE, "whale-member/api/member/getMemberInfo", {
params: { token: this.token },
});
this.member = result?.data || this.member || {};
this.saveCachedToken();
return this.member;
}
async memberInfo(prefix = "当前") {
try {
const data = await this.getMemberInfo();
const name = data?.nickname || maskPhone(data?.phone) || data?.id || "";
const integral = data?.integral ?? data?.point ?? data?.points ?? data?.score ?? "未知";
$.log(`账号[${this.index}] ${prefix}用户: ${name},积分: ${integral}`);
} catch (e) {
const message = String(e.message || e);
$.log(`账号[${this.index}] 查询用户失败: ${message}`);
if (isTokenError(message)) this.removeCachedToken();
}
}
async getSignStatus() {
const result = await this.request(API_BASE, "api/signIn/integralSignInList", {
method: "POST",
data: { token: this.token },
form: true,
});
return result?.data || {};
}
async signIn() {
try {
const status = await this.getSignStatus();
if (status?.signIs) {
$.log(`账号[${this.index}] 今日已签到`);
return;
}
const result = await this.request(API_BASE, "api/signIn/integralSignIn", {
method: "POST",
data: {},
form: true,
});
$.log(`账号[${this.index}] 签到成功: ${result?.msg || "成功"}`);
} catch (e) {
const message = String(e.message || e);
if (/已签到|已签|重复/.test(message)) {
$.log(`账号[${this.index}] 今日已签到`);
return;
}
$.log(`账号[${this.index}] 签到失败: ${message}`);
if (isTokenError(message)) this.removeCachedToken();
}
}
}
!(async () => {
await getNotice();
$.checkEnv(ckName);
for (const user of $.userList) {
await new Task(user).run();
}
})()
.catch((e) => console.log(e))
.finally(() => $.done());
async function getNotice() {
try {
const options = {
url: "https://ghproxy.net/https://raw.githubusercontent.com/smallfawn/Note/refs/heads/main/Notice.json",
headers: {
"User-Agent": defaultUserAgent,
},
timeout: 3000,
};
const { data: res } = await axios.request(options);
$.log(res);
return res;
} catch (e) {}
}
-272
View File
@@ -1,272 +0,0 @@
/*
------------------------------------------
@Author: sm
@Date: 2026.06.01
@Description: 植白说小程序
cron: 30 8 * * *
------------------------------------------
#Notice:
变量名称:zbs
变量值:wx_server 里的 openid/账号标识,多账号用 & 或换行
需要配置:wx_server_url、wx_auth
⚠️【免责声明】
------------------------------------------
1、此脚本仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。
2、由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。
3、请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。
4、此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。
5、本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。
6、如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我们将在收到认证文件确认后删除此脚本。
7、所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。
*/
const { Env } = require("../tools/env.js");
const $ = new Env("植白说小程序");
const axios = require("axios");
const fs = require("fs");
const path = require("path");
const WeChatServer = require("../wxapp/wcs.js");
const ckName = "zbs";
const MINI_APP_ID = "wx6b6c5243359fe265";
const API_BASE = "https://www.kozbs.com/demo/wx/";
const TOKEN_CACHE_FILE = path.join(__dirname, "zbs_token_cache.json");
const USER_AGENT = "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 WindowsWechat(0x63090a13) UnifiedPCWindowsWechat(0xf254173b) XWEB/19027";
const defaultUserAgent = USER_AGENT;
const wechat = new WeChatServer({
url: process.env.wx_server_url || "http://192.168.31.196:8787",
appid: MINI_APP_ID,
auth: process.env.wx_auth || "",
});
function readTokenCache() {
try {
if (!fs.existsSync(TOKEN_CACHE_FILE)) return {};
return JSON.parse(fs.readFileSync(TOKEN_CACHE_FILE, "utf8")) || {};
} catch (e) {
return {};
}
}
function writeTokenCache(cache) {
try {
fs.writeFileSync(TOKEN_CACHE_FILE, JSON.stringify(cache, null, 2), "utf8");
} catch (e) {
$.log(`写入token缓存失败: ${e.message || e}`);
}
}
function shortToken(token = "") {
const value = String(token);
return value ? `${value.slice(0, 6)}***${value.slice(-6)}` : "";
}
function maskPhone(phone = "") {
return String(phone).replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2");
}
function isTokenError(message) {
return /(^|[^0-9])501([^0-9]|$)|token|登录|授权|未登录|失效|过期/i.test(String(message || ""));
}
class Task {
constructor(openid) {
this.index = $.userIdx++;
this.openid = String(openid || "").trim();
this.token = "";
this.userInfo = {};
}
async run() {
const cached = this.getCachedToken();
if (cached?.token) {
this.token = cached.token;
this.userInfo = cached.userInfo || {};
$.log(`账号[${this.index}] 使用缓存token: ${shortToken(this.token)}`);
if (!(await this.checkToken())) {
this.removeCachedToken();
$.log(`账号[${this.index}] 缓存token失效,重新登录`);
}
}
if (!this.token) {
await this.loginByWxCode();
if (!this.token) return;
}
await this.getPoints("当前");
await this.signIn();
await this.getPoints("签到后");
}
get userId() {
return this.userInfo?.userId || this.userInfo?.id || 1;
}
getCachedToken() {
const cache = readTokenCache();
return cache[this.openid] || null;
}
saveCachedToken() {
if (!this.token) return;
const cache = readTokenCache();
cache[this.openid] = {
token: this.token,
userInfo: this.userInfo,
updatedAt: new Date().toISOString(),
};
writeTokenCache(cache);
}
removeCachedToken() {
const cache = readTokenCache();
if (cache[this.openid]) {
delete cache[this.openid];
writeTokenCache(cache);
}
this.token = "";
this.userInfo = {};
}
headers(extra = {}) {
const headers = {
"Content-Type": "application/json",
"User-Agent": USER_AGENT,
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Referer": "https://servicewechat.com/wx6b6c5243359fe265/171/page-frame.html",
"xweb_xhr": "1",
...extra,
};
if (this.token) headers["X-Dts-Token"] = this.token;
return headers;
}
async request(apiPath, { method = "GET", data = {}, auth = true } = {}) {
const options = {
method,
url: new URL(apiPath, API_BASE).toString(),
headers: this.headers(),
timeout: 15000,
validateStatus: () => true,
};
if (!auth) delete options.headers["X-Dts-Token"];
if (method.toUpperCase() === "GET") options.params = data;
else options.data = data;
const { data: result, status } = await axios.request(options);
if (status !== 200) throw new Error(`HTTP ${status}: ${JSON.stringify(result)}`);
if (Number(result?.errno) !== 0) throw new Error(`${result?.errno ?? ""} ${result?.errmsg || result?.message || JSON.stringify(result)}`.trim());
return result;
}
async getLoginCode() {
if (!process.env.wx_auth) throw new Error("缺少 wx_auth,无法从 wx_server 获取 code");
const { data } = await wechat.getCode(this.openid);
const code = data?.code || data?.data?.code;
if (!code) throw new Error(`wx_server 未返回 code: ${JSON.stringify(data)}`);
return code;
}
async loginByWxCode() {
try {
const code = await this.getLoginCode();
const result = await this.request("auth/login_by_weixin", {
method: "POST",
auth: false,
data: {
code,
userInfo: {},
shareUserId: 1,
},
});
const token = result?.data?.token || "";
if (!token) throw new Error(`登录响应未返回token: ${JSON.stringify(result)}`);
this.token = token;
this.userInfo = result?.data?.userInfo || {};
this.saveCachedToken();
$.log(`账号[${this.index}] 登录成功: ${this.userInfo.nickname || maskPhone(this.userInfo.mobile) || this.userId}`);
} catch (e) {
$.log(`账号[${this.index}] 登录失败: ${e.message || e}`);
}
}
async checkToken() {
try {
await this.request("user/getUserIntegral", {
data: { userId: this.userId },
});
return true;
} catch (e) {
return false;
}
}
async getPoints(prefix = "当前") {
try {
const result = await this.request("user/getUserIntegral", {
data: { userId: this.userId },
});
const integer = result?.data?.integer ?? result?.data?.integral ?? result?.data?.point ?? "未知";
$.log(`🌸账号[${this.index}] ${prefix}积分: ${integer}🎉`);
} catch (e) {
const message = String(e.message || e);
$.log(`🌸账号[${this.index}] 获取积分失败: ${message}❌`);
if (isTokenError(message)) this.removeCachedToken();
}
}
async signIn() {
try {
const status = await this.request("home/signDay", {
data: { userId: this.userId },
});
if (status?.data?.isSign) {
$.log(`🌸账号[${this.index}] 今日已签到`);
return;
}
await this.request("home/sign", {
data: { userId: this.userId },
});
$.log(`🌸账号[${this.index}] 签到成功🎉`);
} catch (e) {
const message = String(e.message || e);
if (/已签到|已签|重复/.test(message)) {
$.log(`🌸账号[${this.index}] 今日已签到`);
return;
}
$.log(`🌸账号[${this.index}] 签到失败: ${message}❌`);
if (isTokenError(message)) this.removeCachedToken();
}
}
}
!(async () => {
await getNotice();
$.checkEnv(ckName);
for (const user of $.userList) {
await new Task(user).run();
}
})()
.catch((e) => console.log(e))
.finally(() => $.done());
async function getNotice() {
try {
const options = {
url: "https://ghproxy.net/https://raw.githubusercontent.com/smallfawn/Note/refs/heads/main/Notice.json",
headers: {
"User-Agent": defaultUserAgent,
},
timeout: 3000,
};
const { data: res } = await axios.request(options);
$.log(res);
return res;
} catch (e) {}
}