|
|
@@ -0,0 +1,406 @@
|
|
|
+/*
|
|
|
+小芒商城助手
|
|
|
+
|
|
|
+[Script]
|
|
|
+# > 小芒商城助手
|
|
|
+^https?:\/\/*\.api\.mgtv\.com url script-request-body https://git.jojo21.top/shawenguan/Quantumult-X/raw/master/Scripts/mangguo/mgecomHelper.js
|
|
|
+^https?:\/\/*\.api\.mgtv\.com url script-response-body https://git.jojo21.top/shawenguan/Quantumult-X/raw/master/Scripts/mangguo/mgecomHelper.js
|
|
|
+
|
|
|
+[MITM]
|
|
|
+hostname = api.mgtv.com
|
|
|
+
|
|
|
+*/
|
|
|
+
|
|
|
+const scriptName = `小芒商城助手`;
|
|
|
+const magicJS = MagicJS(scriptName, "INFO");
|
|
|
+
|
|
|
+const MGEcomConstKey = {
|
|
|
+ AllUserInfo: 'mgeComAllUserInfo',
|
|
|
+ AllUserConfig: 'mgeComAllUserConfig',
|
|
|
+};
|
|
|
+
|
|
|
+const gUserAgent = `Mozilla/5.0 (iPhone; CPU iPhone OS 16_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Html5Plus/1.0 (Immersed/20) uni-app`;
|
|
|
+const gHost = `api.wubian.pro`;
|
|
|
+let gToken = magicJS.data.read(WuBianConstKey.Token, '');
|
|
|
+const gCommonHeaders = {
|
|
|
+ 'Accept': `*/*`,
|
|
|
+ 'Accept-Encoding': `gzip, deflate, br`,
|
|
|
+ 'Connection': `keep-alive`,
|
|
|
+ 'Content-Type': `application/json`,
|
|
|
+ 'Cookie': `token=${gToken}`,
|
|
|
+ 'Host': gHost,
|
|
|
+ 'User-Agent': gUserAgent,
|
|
|
+ 'CLIENT-TYPE': `APP`,
|
|
|
+ 'token': gToken,
|
|
|
+ 'Accept-Language': `zh-CN,zh-Hans;q=0.9`
|
|
|
+};
|
|
|
+
|
|
|
+let gRetBody;
|
|
|
+
|
|
|
+let gAllStateData = {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+let SignHelper = (function () {
|
|
|
+ const goldenRatio = 2654435769;
|
|
|
+ function decodeByteArray(byteArray, hasLengthPrefix) {
|
|
|
+ const length = byteArray.length;
|
|
|
+ let totalLength = length << 2;
|
|
|
+
|
|
|
+ if (hasLengthPrefix) {
|
|
|
+ const lastByte = byteArray[length - 1];
|
|
|
+ if (totalLength -= 4, lastByte < totalLength - 3 || lastByte > totalLength) return "";
|
|
|
+ totalLength = lastByte;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let i = 0; i < length; i++) {
|
|
|
+ byteArray[i] = String.fromCharCode(255 & byteArray[i], byteArray[i] >>> 8 & 255, byteArray[i] >>> 16 & 255, byteArray[i] >>> 24 & 255);
|
|
|
+ }
|
|
|
+
|
|
|
+ const resultString = byteArray.join("");
|
|
|
+ return hasLengthPrefix ? resultString.substring(0, totalLength) : resultString;
|
|
|
+ }
|
|
|
+
|
|
|
+ function stringToByteArray(str, hasLengthPrefix) {
|
|
|
+ const length = str.length;
|
|
|
+ let byteArray, arrayLength = length >> 2;
|
|
|
+
|
|
|
+ if (length & 3 !== 0) arrayLength++;
|
|
|
+ if (hasLengthPrefix) {
|
|
|
+ byteArray = new Array(arrayLength + 1);
|
|
|
+ byteArray[arrayLength] = length;
|
|
|
+ } else {
|
|
|
+ byteArray = new Array(arrayLength);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let i = 0; i < length; ++i) {
|
|
|
+ byteArray[i >> 2] |= str.charCodeAt(i) << ((3 & i) << 3);
|
|
|
+ }
|
|
|
+
|
|
|
+ return byteArray;
|
|
|
+ }
|
|
|
+
|
|
|
+ function unsignedRightShift(value) {
|
|
|
+ return 4294967295 & value;
|
|
|
+ }
|
|
|
+
|
|
|
+ function customHashFunction(value, key, accumulator, index, arrayLength, keys) {
|
|
|
+ return (accumulator >>> 5 ^ key << 2) + (key >>> 3 ^ accumulator << 4) ^ (value ^ key) + (keys[3 & index ^ arrayLength] ^ accumulator);
|
|
|
+ }
|
|
|
+
|
|
|
+ function ensureLength(byteArray) {
|
|
|
+ if (byteArray.length < 4) byteArray.length = 4;
|
|
|
+ return byteArray;
|
|
|
+ }
|
|
|
+
|
|
|
+ function encodeByteArray(byteArray, keys) {
|
|
|
+ const length = byteArray.length;
|
|
|
+ const lastIndex = length - 1;
|
|
|
+ let temp, prevByte, nextByte;
|
|
|
+ let sum = 0;
|
|
|
+
|
|
|
+ for (prevByte = byteArray[lastIndex], sum = 0 | Math.floor(6 + 52 / length); sum > 0; --sum) {
|
|
|
+ sum = unsignedRightShift(sum + goldenRatio);
|
|
|
+ nextByte = sum >>> 2 & 3;
|
|
|
+
|
|
|
+ for (let i = 0; i < lastIndex; ++i) {
|
|
|
+ temp = byteArray[i + 1];
|
|
|
+ prevByte = byteArray[i] = unsignedRightShift(byteArray[i] + customHashFunction(sum, temp, prevByte, i, nextByte, keys));
|
|
|
+ }
|
|
|
+
|
|
|
+ temp = byteArray[0];
|
|
|
+ prevByte = byteArray[lastIndex] = unsignedRightShift(byteArray[lastIndex] + customHashFunction(sum, temp, prevByte, lastIndex, nextByte, keys));
|
|
|
+ }
|
|
|
+
|
|
|
+ return byteArray;
|
|
|
+ }
|
|
|
+
|
|
|
+ function utf8Encode(str) {
|
|
|
+ if (/^[\x00-\x7f]*$/.test(str)) return str;
|
|
|
+
|
|
|
+ const utf8Str = [];
|
|
|
+ const length = str.length;
|
|
|
+
|
|
|
+ for (let i = 0, j = 0; i < length; ++i, ++j) {
|
|
|
+ const charCode = str.charCodeAt(i);
|
|
|
+
|
|
|
+ if (charCode < 128) {
|
|
|
+ utf8Str[j] = str.charAt(i);
|
|
|
+ } else if (charCode < 2048) {
|
|
|
+ utf8Str[j] = String.fromCharCode(192 | charCode >> 6, 128 | 63 & charCode);
|
|
|
+ } else {
|
|
|
+ if (charCode >= 55296 && charCode <= 57343) {
|
|
|
+ if (i + 1 < length) {
|
|
|
+ const nextCharCode = str.charCodeAt(i + 1);
|
|
|
+ if (charCode < 56320 && 56320 <= nextCharCode && nextCharCode <= 57343) {
|
|
|
+ const surrogatePair = 65536 + ((1023 & charCode) << 10 | 1023 & nextCharCode);
|
|
|
+ utf8Str[j] = String.fromCharCode(240 | surrogatePair >> 18 & 63, 128 | surrogatePair >> 12 & 63, 128 | surrogatePair >> 6 & 63, 128 | 63 & surrogatePair);
|
|
|
+ ++i;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw new Error("Malformed string");
|
|
|
+ }
|
|
|
+ utf8Str[j] = String.fromCharCode(224 | charCode >> 12, 128 | charCode >> 6 & 63, 128 | 63 & charCode);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return utf8Str.join("");
|
|
|
+ }
|
|
|
+
|
|
|
+ function encryptString(str, key) {
|
|
|
+ if (str.length === 0) return str;
|
|
|
+ str = utf8Encode(str);
|
|
|
+ key = utf8Encode(key);
|
|
|
+
|
|
|
+ return decodeByteArray(encodeByteArray(stringToByteArray(str, true), ensureLength(stringToByteArray(key, false))), false);
|
|
|
+ }
|
|
|
+
|
|
|
+ function base64Encode(str1, str2) {
|
|
|
+ return btoa(encryptString(str1, str2));
|
|
|
+ }
|
|
|
+
|
|
|
+ function generateCustomString(str1, str2) {
|
|
|
+ const md5 = createWMd5();
|
|
|
+ return md5.hex_md5_32(`string xiaomang = "${str1}|${str2}";`);
|
|
|
+ }
|
|
|
+
|
|
|
+ function generateUuidWithVersion(uuid, version, offset) {
|
|
|
+ const data = {
|
|
|
+ uuid: uuid,
|
|
|
+ reunixtime: Math.round((Date.now() - offset) / 1000),
|
|
|
+ };
|
|
|
+ const customString = generateCustomString(uuid, version);
|
|
|
+ return base64Encode(JSON.stringify(data), customString);
|
|
|
+ }
|
|
|
+
|
|
|
+ function generateAnotherCustomString(str1, str2) {
|
|
|
+ const md5 = createWMd5();
|
|
|
+ return md5.hex_md5_32(`string ma = "${str1}|${str2}";`);
|
|
|
+ }
|
|
|
+
|
|
|
+ function generateRandomSalt() {
|
|
|
+ const multiplier = 38833;
|
|
|
+ const randomValue = Math.floor(Math.random() * 2 ** 32) + 1;
|
|
|
+ return randomValue * multiplier;
|
|
|
+ }
|
|
|
+
|
|
|
+ function generateSignedUuid(uuid, version, offset, maBig, maSi) {
|
|
|
+ let i = 0;
|
|
|
+ let signedUuid = "";
|
|
|
+
|
|
|
+ while (!(signedUuid <= maBig && signedUuid.includes(maSi))) {
|
|
|
+ if (++i > 100000) return "";
|
|
|
+ signedUuid = generateUuidWithSalt(uuid, version, offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(i);
|
|
|
+ return signedUuid;
|
|
|
+ }
|
|
|
+
|
|
|
+ function generateUuidWithSalt(uuid, version, offset) {
|
|
|
+ const data = {
|
|
|
+ uuid: uuid,
|
|
|
+ reunixtime: Math.round((Date.now() - offset) / 1000),
|
|
|
+ salt: generateRandomSalt(),
|
|
|
+ };
|
|
|
+ const customString = generateAnotherCustomString(uuid, version);
|
|
|
+ return base64Encode(JSON.stringify(data), customString);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function getDySign(uuid) {
|
|
|
+ let stateData = await getStateData();
|
|
|
+ const { userConfig, userInfo, diffTime } = stateData;
|
|
|
+ return generateUuidWithVersion(userInfo.uuid, userConfig.version, diffTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ async function getMaSign(uuid) {
|
|
|
+ let stateData = await getStateData();
|
|
|
+ const { userConfig, userInfo, diffTime } = stateData;
|
|
|
+ return generateSignedUuid(userInfo.uuid || "", userConfig.version, diffTime, userConfig.version_ma_big, userConfig.version_ma_si);
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ getDySign: getDySign,
|
|
|
+ getMaSign: getMaSign,
|
|
|
+ };
|
|
|
+
|
|
|
+})();
|
|
|
+
|
|
|
+
|
|
|
+async function Main() {
|
|
|
+ if (magicJS.isStrictRequest) {
|
|
|
+ magicJS.checkRecordRequestBody();
|
|
|
+ }
|
|
|
+ if (magicJS.isRequest) {
|
|
|
+ checkHandleRequest();
|
|
|
+ } else {
|
|
|
+ updateHeaders();
|
|
|
+ }
|
|
|
+ magicJS.notification.msg('');
|
|
|
+ if (gRetBody) {
|
|
|
+ magicJS.done({
|
|
|
+ body: JSON.stringify(gRetBody)
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ magicJS.done();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function checkHandleRequest() {
|
|
|
+ const url = $request.url;
|
|
|
+ const path = $request.path;
|
|
|
+ magicJS.logger.info(`请求url=${url}#${$request.method}`);
|
|
|
+ magicJS.logger.info(`请求body=${magicJS.getRequestBody()}`);
|
|
|
+ if ($request && $request.method != 'OPTIONS') {
|
|
|
+ switch (path) {
|
|
|
+ case '/GetUserInfo':
|
|
|
+ handleUserInfo();
|
|
|
+ break;
|
|
|
+ case '/user/home/info':
|
|
|
+ handleUserHomeInfo();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handleHeaders() {
|
|
|
+}
|
|
|
+
|
|
|
+function updateHeaders() {
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function getResponsePlainData() {
|
|
|
+ let data = magicJS.getResponseBody();
|
|
|
+ if (!data) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return JSON.parse(data);
|
|
|
+ } catch (err) {
|
|
|
+ magicJS.logger.info(data);
|
|
|
+ magicJS.logger.error(err);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function getRequestPlainData() {
|
|
|
+ let data = magicJS.getRequestBody();
|
|
|
+ let reqData = null;
|
|
|
+ if (data) {
|
|
|
+ reqData = JSON.parse(data);
|
|
|
+ } else {
|
|
|
+ reqData = magicJS.parseQueryStr($request.url);
|
|
|
+ }
|
|
|
+ return reqData;
|
|
|
+}
|
|
|
+
|
|
|
+function getQueryPlainData() {
|
|
|
+ let params = magicJS.parseQueryStr($request.url);
|
|
|
+ return params;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function handleUserInfo() {
|
|
|
+ let rspData = getResponsePlainData();
|
|
|
+ if (!rspData) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (rspData.code != 200) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ let allUserDict = magicJS.data.read(MGEcomConstKey.AllUserInfo, {});
|
|
|
+ const userInfo = rspData.data;
|
|
|
+ if (userInfo) {
|
|
|
+ let uuid = userInfo.uuid;
|
|
|
+ let mobile = userInfo.relate_mobile;
|
|
|
+ if (userInfo.ticket) {
|
|
|
+ allUserDict[uuid] = userInfo;
|
|
|
+ allUserDict[mobile] = userInfo;
|
|
|
+ setDiffTime(Date.now() - 1e3 * homeInfo.unixtime);
|
|
|
+ } else {
|
|
|
+ delete allUserDict[uuid];
|
|
|
+ delete allUserDict[mobile];
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ let reqData = getQueryPlainData();
|
|
|
+ let uuid = reqData.uuid;
|
|
|
+ let oldUserInfo = allUserDict[uuid];
|
|
|
+ if (oldUserInfo) {
|
|
|
+ let mobile = oldUserInfo.relate_mobile;
|
|
|
+ delete allUserDict[uuid];
|
|
|
+ delete allUserDict[mobile];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ magicJS.data.write(MGEcomConstKey.AllUserInfo, allUserDict);
|
|
|
+}
|
|
|
+
|
|
|
+function handleUserHomeInfo() {
|
|
|
+ let rspData = getResponsePlainData();
|
|
|
+ if (!rspData) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (rspData.code != 200) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const homeInfo = rspData.data;
|
|
|
+
|
|
|
+ let allConfigDict = magicJS.data.read(MGEcomConstKey.AllUserConfig, {});
|
|
|
+ let reqData = getQueryPlainData();
|
|
|
+ let uuid = reqData.uuid;
|
|
|
+ allConfigDict[uuid] = homeInfo;
|
|
|
+ magicJS.data.write(MGEcomConstKey.AllUserConfig, allConfigDict);
|
|
|
+
|
|
|
+ setDiffTime(Date.now() - 1e3 * homeInfo.unixtime);
|
|
|
+}
|
|
|
+
|
|
|
+async function getStateData(uuid) {
|
|
|
+ let state = gAllStateData[uuid];
|
|
|
+ if (!state) {
|
|
|
+ state = {
|
|
|
+ diffTime: 0,
|
|
|
+ userInfo: null,
|
|
|
+ userConfig: null,
|
|
|
+ };
|
|
|
+ gAllStateData[uuid] = state;
|
|
|
+ };
|
|
|
+ if (!state.userInfo) {
|
|
|
+ state.userConfig = await getUserConfig(uuid);
|
|
|
+ }
|
|
|
+ if (!state.userInfo) {
|
|
|
+ state.userInfo = await getUserInfo(uuid);
|
|
|
+ }
|
|
|
+ return state;
|
|
|
+}
|
|
|
+
|
|
|
+async function getUserConfig(uuid) {
|
|
|
+ let allUserDict = magicJS.data.read(MGEcomConstKey.AllUserInfo, {});
|
|
|
+ let userConfig = allUserDict[uuid];
|
|
|
+ if (!userConfig) {
|
|
|
+
|
|
|
+ }
|
|
|
+ return userConfig;
|
|
|
+}
|
|
|
+
|
|
|
+async function getUserInfo(uuid) {
|
|
|
+ let allConfigDict = magicJS.data.read(MGEcomConstKey.AllUserConfig, {});
|
|
|
+ let userInfo = allConfigDict[uuid];
|
|
|
+ if (!userInfo) {
|
|
|
+
|
|
|
+ }
|
|
|
+ return userInfo;
|
|
|
+}
|
|
|
+
|
|
|
+async function getDiffTime() {
|
|
|
+ return 0;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+function setDiffTime(diffTime) {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+Main().catch((e) => magicJS.logger.log(`-\n ${e}`)).finally(() => magicJS.done());
|