QLScriptPublic/wxapp/yz9d.js
2026-05-31 14:36:06 +08:00

271 lines
8.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
------------------------------------------
@Author: sm
@Date: 2026.05.31
@Description: 戴可思小程序签到
cron: 25 8 * * *
------------------------------------------
变量名dks
变量值wx_server 里的 openid/账号标识,多账号用 & 或换行
------------------------------------------
*/
const { Env } = require("../tools/env.js");
const $ = new Env("戴可思小程序签到");
const axios = require("axios");
const fs = require("fs");
const path = require("path");
const WeChatServer = require("./wcs.js");
const MINI_APP_ID = "wx9d46d96c4c35a53a";
const CLIENT_BIZ = "weapp_wsc";
const KDT_ID = "46323516";
const USER_VERSION = "2.233.4.101";
const PAGE_VERSION = "96";
const API_BASE = "https://h5.youzan.com";
const TOKEN_CACHE_FILE = path.join(__dirname, "dks_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";
let ckName = "dks";
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 maskPhone(phone = "") {
return String(phone).replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2");
}
function pickToken(data = {}) {
return data.accessToken || data.access_token || "";
}
class Task {
constructor(openid) {
this.index = $.userIdx++;
this.openid = String(openid || "").trim();
this.token = "";
this.sessionId = "";
this.cookie = "";
this.kdtId = KDT_ID;
this.userInfo = {};
}
async run() {
const cached = this.getCachedToken();
if (cached) {
this.applyToken(cached);
$.log(`账号[${this.index}] 使用缓存token`);
if (!(await this.checkToken())) {
this.removeCachedToken();
$.log(`账号[${this.index}] 缓存token失效重新登录`);
}
}
if (!this.token) {
await this.loginByWxCode();
if (!this.token) return;
}
await this.showCheckinPage();
await this.doCheckin();
await this.getPoints();
}
getCachedToken() {
const cache = readTokenCache();
return cache[this.openid] || null;
}
saveCachedToken() {
if (!this.token) return;
const cache = readTokenCache();
cache[this.openid] = {
accessToken: this.token,
sessionId: this.sessionId,
kdtId: this.kdtId,
cookie: this.cookie,
mobile: this.userInfo.mobile || "",
nickName: this.userInfo.nick_name || this.userInfo.nickName || "",
updatedAt: new Date().toISOString(),
};
writeTokenCache(cache);
}
removeCachedToken() {
const cache = readTokenCache();
if (cache[this.openid]) {
delete cache[this.openid];
writeTokenCache(cache);
}
this.token = "";
this.sessionId = "";
this.cookie = "";
}
applyToken(data = {}) {
this.token = pickToken(data);
this.sessionId = data.sessionId || data.session_id || "";
this.kdtId = String(data.kdtId || data.kdt_id || KDT_ID);
this.cookie = data.cookie || "";
}
getHeaders(extra = {}) {
const headers = {
"User-Agent": USER_AGENT,
"Referer": `https://servicewechat.com/${MINI_APP_ID}/${PAGE_VERSION}/page-frame.html`,
"Accept": "*/*",
"Extra-Data": JSON.stringify({
sid: this.sessionId || "",
version: USER_VERSION,
clientType: "weapp-miniprogram",
client: "weapp",
bizEnv: "wsc",
}),
...extra,
};
if (this.cookie) headers.Cookie = this.cookie;
return headers;
}
getBaseParams(params = {}) {
return {
app_id: MINI_APP_ID,
kdt_id: this.kdtId,
access_token: this.token,
...params,
};
}
async request({ method = "GET", path: apiPath, params = {}, data = {}, skipToken = false }) {
const options = {
method,
url: `${API_BASE}${apiPath.startsWith("/") ? apiPath : `/${apiPath}`}`,
headers: this.getHeaders(method === "POST" ? { "Content-Type": "application/json" } : {}),
timeout: 15000,
validateStatus: () => true,
};
options.params = skipToken ? params : this.getBaseParams(params);
if (method !== "GET") options.data = data;
const { data: result, status, headers } = await axios.request(options);
if (headers["set-cookie"]) {
this.cookie = headers["set-cookie"].map((item) => item.split(";")[0]).join("; ");
}
if (status !== 200) throw new Error(`HTTP ${status}: ${JSON.stringify(result)}`);
if (!result || result.code !== 0) 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: "/wscshop/weapp/authorize.json",
skipToken: true,
data: {
appId: MINI_APP_ID,
clientBiz: CLIENT_BIZ,
code,
},
});
this.applyToken(data);
this.userInfo = data || {};
this.saveCachedToken();
$.log(`账号[${this.index}] 登录成功: ${data.nick_name || data.nickName || ""} ${maskPhone(data.mobile) || ""}`);
} catch (e) {
$.log(`账号[${this.index}] 登录失败: ${e.message || e}`);
}
}
async checkToken() {
try {
const data = await this.request({ path: "/wscump/integral/user_points.json" });
this.points = data?.current_points ?? data?.real_points;
return true;
} catch (e) {
return false;
}
}
async showCheckinPage() {
try {
const data = await this.request({ path: "/wscump/checkin/show_checkin_page_v2.json" });
this.checkinId = data?.checkinId;
this.isShow = !!data?.isShow;
$.log(`账号[${this.index}] 签到活动: checkinId=${this.checkinId || "未获取"} isShow=${this.isShow}`);
} catch (e) {
$.log(`账号[${this.index}] 获取签到活动失败: ${e.message || e}`);
if (/access_token|token|登录|授权|invalid session/i.test(String(e.message || e))) this.removeCachedToken();
}
}
async doCheckin() {
if (!this.checkinId) {
$.log(`账号[${this.index}] 未获取到 checkinId跳过签到`);
return;
}
try {
const data = await this.request({
path: "/wscump/checkin/checkinV2.json",
params: { checkinId: this.checkinId },
});
const awards = (data?.list || []).map((item) => item?.infos?.title).filter(Boolean).join(", ");
$.log(`账号[${this.index}] 签到成功: ${data?.desc || ""}${awards ? ` ${awards}` : ""}`);
} catch (e) {
const message = String(e.message || e);
if (/已达最大参与次数|已签到|重复签到/.test(message)) {
$.log(`账号[${this.index}] 今日已签到`);
return;
}
$.log(`账号[${this.index}] 签到失败: ${message}`);
if (/access_token|token|登录|授权|invalid session/i.test(message)) this.removeCachedToken();
}
}
async getPoints() {
try {
const data = await this.request({ path: "/wscump/integral/user_points.json" });
$.log(`账号[${this.index}] 当前积分: ${data?.current_points ?? data?.real_points ?? "未知"}`);
} catch (e) {
$.log(`账号[${this.index}] 查询积分失败: ${e.message || e}`);
}
}
}
!(async () => {
$.checkEnv(ckName);
for (const openid of $.userList) {
await new Task(openid).run();
}
})()
.catch((e) => $.log(e.message || e))
.finally(() => $.done());