// # Source: https://github.com/sudojia/AutoTaskScript/blob/script/src/client/sudojia_xiangwan.js // # Raw: https://raw.githubusercontent.com/sudojia/AutoTaskScript/script/src/client/sudojia_xiangwan.js // # Repo: sudojia/AutoTaskScript // # Path: src/client/sudojia_xiangwan.js // # UploadedAt: 2025-04-28T19:26:13+08:00 // # SHA256: 659341f6d90da24ae84aff1ca890993bb08f62f22537ed7eca9ee139ad06e61d // # Category: APP版/账密 // # Evidence: username/password/mobile/login // /** * 享玩游戏APP * 下载链接:https://dl.duoyou.com/fenapk/xwshare/xw_100016.apk?version=1.3 * * 会员v0:起始签到 10 个玩豆,连续签到递增 1 玩豆,最高签到 15 个玩豆 * 会员v1-2:起始 15,最高 20 * ... * 会员v10:起始 75,最高 80 * 具体自行查看签到规则 * * 以会员v0来计算(包含连签奖励): * 10 玩豆 兑换 0.1 现金, * 500 玩豆 兑换 5 现金,大约需 21 天 * 1000 玩豆 兑换 10 现金,大约需 30 天 * 5000 玩豆 兑换 50 现金,大约需 200 天 * 10000 玩豆 兑换 100现金,大约需 381 天 * 30000 玩豆 兑换 300 现金,大约需 1135 天 😂😂😂 * * export XWGAME_ACCOUNTS = '手机号#密码' * 多账号用 & 或换行 * * @author Telegram@sudojia * @site https://blog.imzjw.cn * @date 2025/02/25 * * const $ = new Env('享玩游戏APP') * cron: 33 0 * * * */ const initScript = require('../utils/initScript') const {$, notify, sudojia, checkUpdate} = initScript('享玩游戏APP'); const crypto = require('crypto'); const moment = require('moment'); const fs = require('fs').promises; const path = require('path'); const xiangWanList = process.env.XWGAME_ACCOUNTS ? process.env.XWGAME_ACCOUNTS.split(/[\n&]/) : []; // 消息推送 let message = ''; // token 缓存地址 const TOKEN_CACHE_PATH = path.join(__dirname, 'xiangwan_token_cache.json'); // 接口地址 const baseUrl = 'https://sdk-api.xiangwan.com' // 加密秘钥(Base64) const keyBase64 = '5snOI0E5WUpP99pG5+fzug=='; // 解码 Base64 得到原始的二进制密钥 const key = Buffer.from(keyBase64, 'base64'); // 加密 Iv(Base64) const ivBase64 = 'KhVRUtkYTNM82dSDxb7/YQ=='; // 解码 Base64 得到原始的二进制 IV const iv = Buffer.from(ivBase64, 'base64'); // 请求头 const headers = { 'User-Agent': 'okhttp/4.11.0', 'Accept-Encoding': 'gzip', 'Content-Type': 'application/x-www-form-urlencoded' }; !(async () => { await checkUpdate($.name, xiangWanList); for (let i = 0; i < xiangWanList.length; i++) { const index = i + 1; const [account, pwd] = xiangWanList[i].split('#'); console.log(`\n*****第[${index}]个${$.name}账号*****`); await login(account, pwd); if (!$.isLogin) { continue; } await $.wait(sudojia.getRandomWait(1e3, 2e3)); message += `📣====${$.name}账号[${index}]====📣\n`; await main(); await $.wait(sudojia.getRandomWait(2000, 2500)); } if (message) { await notify.sendNotify(`「${$.name}」`, `${message}`); } })().catch((e) => $.logErr(e)).finally(() => $.done()); async function main() { await sign(); await $.wait(sudojia.getRandomWait(1e3, 2e3)); await getUserInfo(); } /** * 登录 * * @param account * @param password * @returns {Promise} */ async function login(account, password) { try { let cachedToken = await getTokenFromCache("xiangwan", account); if (cachedToken) { headers.authorization = `Bearer ${cachedToken}`; console.log('使用缓存 token 登录,验证中...'); const isValid = await validateToken(cachedToken); await $.wait(sudojia.getRandomWait(2e3, 3e3)); if (isValid) { $.isLogin = true; console.log('token 验证有效✅'); return; } else { console.log('缓存 token 验证失效,开始重新登录更新 token...'); } } const encryptData = await sudojia.sendRequest(`${baseUrl}/account/account_login`, 'post', headers, { "params": encryptCredentials(JSON.stringify({ ...getCommonParams(), account: account, password: password })) }); const decryptData = JSON.parse(decryptCredentials(encryptData)); if (200 !== decryptData.status_code) { $.isLogin = false; return console.error(`登录失败 ->`, decryptData); } console.log('登录成功~'); headers.authorization = `Bearer ${decryptData.data.token}`; $.isLogin = true; // 保存 token 到文件 await saveTokenToCache("xiangwan", account, decryptData.data.token); } catch (e) { $.isLogin = false; if (e.response || e.response.data) { console.error(`登录时发生异常 ->`, e.response.data); } else { console.error(`登录时发生异常 ->`, e); } } } /** * 签到 * * @returns {Promise} */ async function sign() { try { console.log('\n开始执行...'); await $.wait(sudojia.getRandomWait(1e3, 2e3)); const days = moment().format('YYYYMMDD'); const encryptData = await sudojia.sendRequest(`${baseUrl}/discover/sign`, 'post', headers, { "params": encryptCredentials(JSON.stringify({ ...getCommonParams(), "days": days })) }); const decryptData = JSON.parse(decryptCredentials(encryptData)); if (200 !== decryptData.status_code) { message += `签到失败 -> ${decryptData.message}\n`; return console.error(`签到失败 ->`, decryptData.message); } console.log(`签到成功,恭喜获得:${decryptData.data.score}玩豆`); message += `签到成功,恭喜获得:${decryptData.data.score}玩豆\n`; } catch (e) { console.error(`签到时发生异常 ->`, e); } } /** * 获取用户信息 * * @returns {Promise} */ async function getUserInfo() { try { const encryptData = await sudojia.sendRequest(`${baseUrl}/me/member_info`, 'post', headers, { "params": encryptCredentials(JSON.stringify({ ...getCommonParams() })) }); const decryptData = JSON.parse(decryptCredentials(encryptData)); if (200 !== decryptData.status_code) { return console.error(`获取用户信息时发生异常 ->`, decryptData); } const {nickname, mobile, balance} = decryptData.data; const hiddenMobile = `${mobile.slice(0, 3)}***${mobile.slice(-3)}` || 18888888888; const displayName = nickname || "吴彦祖"; console.log(`${displayName}(${hiddenMobile})`); console.log(`当前玩豆:${balance}`); message += `${nickname}(${hiddenMobile})\n`; message += `当前玩豆:${balance}\n\n`; } catch (e) { console.error(`获取用户信息时发生异常 ->`, e); } } /** * 验证 token 是否有效 * * @param token * @returns {Promise} */ async function validateToken(token) { try { const encryptData = await sudojia.sendRequest(`${baseUrl}/me/member_account`, 'post', { authorization: `Bearer ${token}` }, { "params": encryptCredentials(JSON.stringify({ ...getCommonParams() })) }); const decryptData = JSON.parse(decryptCredentials(encryptData)); return 200 === decryptData.status_code; } catch (error) { console.error('验证 token 时发生异常:', error); return false; } } /** * 获取公共请求参数 * * @returns {Object} 包含公共请求参数的对象 */ function getCommonParams() { return { "app_channel": "xw_100016", "app_version": "1.3", "invite_uid": "", "app_os": "android", "server_device_id": "176" }; } /** * 加密请求数据 * * @returns {string} 加密后的Base64字符串 */ function encryptCredentials(plainText) { // 创建加密器 const cipher = crypto.createCipheriv('aes-128-cbc', key, iv); // 加密数据 let encrypted = cipher.update(plainText, 'utf8', 'base64'); encrypted += cipher.final('base64'); return encrypted; } /** * 解密响应数据 * * @param encryptedText * @returns {string} */ function decryptCredentials(encryptedText) { // 创建解密器 const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv); // 解密数据 let decrypted = decipher.update(encryptedText, 'base64', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; } /** * 从文件中读取 token * * @param platform * @param username * @returns {Promise<*|null>} */ async function getTokenFromCache(platform, username) { try { try { // 检查文件是否存在 await fs.access(TOKEN_CACHE_PATH); } catch (error) { console.warn('token 缓存文件不存在,创建一个空文件...'); await fs.writeFile(TOKEN_CACHE_PATH, JSON.stringify({}, null, 2), 'utf8'); return null; } const cache = await fs.readFile(TOKEN_CACHE_PATH, 'utf-8'); // 检查文件内容是否为空 if (!cache.trim()) { console.warn('token 缓存文件内容为空,开始重新登录生成 token 内容'); return null; } let cacheObj; try { cacheObj = JSON.parse(cache); } catch (parseError) { console.error('解析 token 缓存文件失败:', parseError); return null; // 解析失败时返回 null } // 检查平台和用户名是否存在 if (cacheObj[platform] && cacheObj[platform][username]) { return cacheObj[platform][username]; } else { console.warn(`token 缓存文件中未找到[${username}]的账号,开始重新登录生成 token`); return null; } } catch (error) { console.error('读取缓存文件失败:', error); return null; } } /** * 将 token 保存到文件 * * @param platform * @param username * @param token * * @returns {Promise} */ async function saveTokenToCache(platform, username, token) { try { let cacheObj = {}; try { const cache = await fs.readFile(TOKEN_CACHE_PATH, 'utf-8'); if (cache.trim()) { cacheObj = JSON.parse(cache); } else { console.warn('token 缓存文件内容为空,开始初始化为空对象...'); } } catch (error) { console.warn('读取缓存文件失败,初始化为空对象:', error); } if (!cacheObj[platform]) { cacheObj[platform] = {}; } cacheObj[platform][username] = token; await fs.writeFile(TOKEN_CACHE_PATH, JSON.stringify(cacheObj, null, 2), 'utf-8'); console.log('token 已保存到缓存文件[xiangwan_token_cache.json]'); console.log(`路径:${TOKEN_CACHE_PATH}`); } catch (error) { console.error('保存 token 失败:', error); } }