diff --git a/CHANGELOG.md b/CHANGELOG.md index bbf1ae7..0704bf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ -# 1.2.2 ~ 1.2.6 +# 1.2.7 +* 增加`#扫码登录`支持扫码获取sk以及ck +* 增加`#账号密码登录`支持获取sk以及ck +# 1.2.0 ~ 1.2.6 * 增加七圣召唤图鉴 * 可通过`#七圣图鉴`获取可查询的目录数据 * 优化`stoken`绑定 @@ -20,11 +23,9 @@ * `#月莲在哪里` 指令类似 `某某物品在哪里` * 感谢 **@小灰灰** 提供的webAPI接口服务 * 自动签到增加推送签到信息以及成功信息 -# 1.2.1 * 新增指令`获取(抽卡记录|ck)` 导出cookie或者抽卡链接供用户操作到其他bot或app上面 * 增加国际服 `stoken` 绑定 (功能目前仅支持V3) * 支持国际服`刷新ck` -# 1.2.0 * 新增指令 `#刷新ck` * 需要绑定`stoken` * 用于刷新登录失效的ck diff --git a/apps/Note.js b/apps/Note.js index 6f1fbc2..76a1d9b 100644 --- a/apps/Note.js +++ b/apps/Note.js @@ -2,18 +2,15 @@ import { segment } from "oicq"; import fetch from "node-fetch"; -import Common from "../components/Common.js"; import fs from "fs"; import { isV3 } from '../components/Changelog.js' -import lodash from "lodash"; import gsCfg from '../model/gsCfg.js' import { Cfg, Data } from "../components/index.js"; -import moment from 'moment'; import utils from "../model/mys/utils.js"; import note from '../model/note.js' import User from "../model/user.js"; diff --git a/apps/index.js b/apps/index.js index eb9fc46..c6fd53a 100644 --- a/apps/index.js +++ b/apps/index.js @@ -23,6 +23,7 @@ import { sysCfg, updateTemp, updateMiaoPlugin } from "./admin.js"; + import { rule as userRule, delSign, @@ -42,13 +43,16 @@ import { cookiesDocHelp, signTask } from "./sign.js" - +import { + rule as topupLoginRule, + qrCodeLogin,UserPassMsg,UserPassLogin, +} from './mhyTopUpLogin.js' export { updateRes, updateTemp, delSign, gcPaylog, - cloudSign, + cloudSign,qrCodeLogin, seach, bindLogin_ticket, - bbsSign, + bbsSign,UserPassMsg,UserPassLogin, gclog, mytoken, getBasicVoide, bindStoken, @@ -112,7 +116,8 @@ let rule = { }, ...userRule, ...signRule, - ...adminRule + ...adminRule, + ...topupLoginRule }; lodash.forEach(rule, (r) => { diff --git a/apps/mhyTopUpLogin.js b/apps/mhyTopUpLogin.js new file mode 100644 index 0000000..395887e --- /dev/null +++ b/apps/mhyTopUpLogin.js @@ -0,0 +1,82 @@ +import { + isV3 +} from '../components/Changelog.js' +import mys from "../model/mhyTopUpLogin.js" +import Common from "../components/Common.js"; +import { bindStoken } from './user.js' +const _path = process.cwd(); +export const rule = { + qrCodeLogin: { + reg: `^#(扫码|二维码|辅助)登录$`, + describe: "扫码登录" + }, + UserPassMsg: { + reg: `^#(账号|密码)(密码)?登录$`, + describe: "账号密码登录" + }, + UserPassLogin: { + reg: `^(.*)$`, + describe: "账号密码登录" + }, + // GetCode: { + // /** 命令正则匹配 */ + // reg: '^#?原神(微信)?充值(微信)?(.*)$', + // /** 执行方法 */ + // describe: '原神充值(离线)' + // }, showgoods: { + // reg: "^#?商品列表", + // fnc: '原神充值商品列表' + // } +} + +export async function qrCodeLogin(e, { render }) { + let Mys = new mys(e) + let res = await Mys.qrCodeLogin() + if (!res?.data) return false; + await Common.render(`qrCode/index`, { + url: res.data.url + }, { + e, + render, + scale: 1.2 + }) + res = await Mys.GetQrCode(res.data.ticket) + if (!res) return true; + await bindSkCK(res) + return true; +} + +export async function UserPassMsg(e) { + let Mys = new mys(e) + await Mys.UserPassMsg() + return true; +} + + +export async function UserPassLogin(e) { + if (!e.isPrivate) { + e.reply("请私聊发送") + return true; + } + let Mys = new mys(e) + let res = await Mys.UserPassLogin(); + if (res) await bindSkCK(e, res) + return true; +} + +export async function bindSkCK(e, res) { + e.msg = res.stoken, e.raw_message = res.stoken + await bindStoken(e) + e.ck = res.cookie, e.msg = res.cookie, e.raw_message = res.cookie; + if (isV3) { + let userck = (await import(`file:///${_path}/plugins/genshin/model/user.js`)).default + e.isPrivate = true + await (new userck(e)).bing() + } else { + let { + bingCookie + } = (await import(`file:///${_path}/lib/app/dailyNote.js`)) + e.isPrivate = true; + await bingCookie(e) + } +} diff --git a/apps/user.js b/apps/user.js index a2edef9..9b1b6c2 100644 --- a/apps/user.js +++ b/apps/user.js @@ -270,8 +270,9 @@ export async function cloudToken(e) { e.reply(`格式支持\nai=*;ci=*;oi=*;ct=***********;si=**************;bi=***********;devId=***********`) return false; } - let msg = e.msg.split("devId") + let msg = e.msg.replace(/dev(i|l|I|L)d/g,'devId').split("devId") if (msg.length < 2) { + Bot.logger.mark(`云原神绑定失败:未包含devId字段~`) return false; } let devId = msg[1].replace(/=/, "") diff --git a/model/mhyTopUpLogin.js b/model/mhyTopUpLogin.js new file mode 100644 index 0000000..d5f73b1 --- /dev/null +++ b/model/mhyTopUpLogin.js @@ -0,0 +1,131 @@ +import User from "./user.js"; +import fs from "fs"; +import { + isV3 +} from '../components/Changelog.js' +import utils from './mys/utils.js'; +import gsCfg from './gsCfg.js'; + +export default class mysTopLogin { + constructor(e) { + this.e = e; + this.init(); + //消息提示以及风险警告 + this.sendMsgUser = `免责声明:您将通过扫码完成获取米游社sk以及ck。\n本Bot以及运营者将不会保存您的登录状态。\n我方仅提供米游社查询及相关游戏内容服务,若您的账号封禁、被盗等处罚与我方无关。\n害怕风险请勿扫码~` + this.sendMsgUserPassLogin = `免责声明:您将通过密码完成获取米游社sk以及ck。\n本Bot以及运营者将不会保存您的账号和密码。\n我方仅提供米游社查询及相关游戏内容服务,若您的账号封禁、被盗等处罚与我方无关。\n害怕风险请勿发送账号密码~` + } + async init() { + this.user = new User(this.e) + } + // + async qrCodeLogin() { + this.device = await utils.randomString(64) + this.e.reply(this.sendMsgUser) + let res = await this.user.getData("qrCodeLogin", { + device: this.device + }) + if (!res.data) { + return false; + } + res.data["ticket"] = res?.data?.url.split("ticket=")[1] + return res + } + async GetQrCode(ticket) { + let res; + for (let n = 1; n < 60; n++) { + await utils.sleepAsync(5000) + res = await this.user.getData("qrCodeQuery", { + device: this.device, ticket + }) + if (res?.data?.stat == "Scanned") { + Bot.logger.mark(`[米哈游登录] ${Bot.logger.blue(JSON.stringify(res))}`) + await this.e.reply("二维码已扫描,请确认登录", true) + } + if (res?.data?.stat == "Confirmed") { + Bot.logger.mark(`[米哈游登录] ${Bot.logger.blue(JSON.stringify(res))}`) + break + } + } + if (!res) { + await this.e.reply("验证超时", true) + return false + } + let raw = JSON.parse(res?.data?.payload?.raw) + let UserData = await this.user.getData("getTokenByGameToken", raw) + let ck = await this.user.getData("getCookieAccountInfoByGameToken", raw) + return { + cookie: `ltoken=${UserData.data.token.token};ltuid=${UserData.data.user_info.aid};cookie_token=${ck.data.cookie_token}`, + stoken: `stoken=${UserData.data.token.token};stuid=${UserData.data.user_info.aid};mid=${UserData.data.user_info.mid}` + } + } + + async UserPassMsg() { + this.e.reply(this.sendMsgUserPassLogin) + this.e.reply(`请将账号密码用逗号隔开发送以完成绑定\n例:xxx@qq.com,xxxxx`) + } + async UserPassLogin() { + let msg = this.e.msg.replace(',|,', ',').split(','); + if (msg.length != 2) { + return false; + } + let body = { + account: msg[0], password: msg[1], + } + let res = await this.user.getData("loginByPassword", body, "") + Bot.logger.mark(`[米哈游登录] ${Bot.logger.blue(JSON.stringify(res))}`) + if (res.retcode == -3101) { + Bot.logger.mark("[米哈游登录] 正在验证") + this.aigis_captcha_data = JSON.parse(res.aigis_data.data) + // let validate = await this.crack_geetest() + let validate = await this.user.getData("validate",this.aigis_captcha_data,false) + if (validate?.data?.validate) { + Bot.logger.mark("[米哈游登录] 验证成功") + } else { + Bot.logger.error("[米哈游登录] 验证失败") + this.e.reply('接口效验失败,请重新尝试~') + return false + } + let aigis= res.aigis_data.session_id + ";" + Buffer.from(JSON.stringify({ + geetest_challenge: validate?.data?.challenge, + geetest_seccode: validate?.data?.validate + "|jordan", + geetest_validate: validate?.data?.validate + })).toString("base64") + body.headers={ + 'x-rpc-aigis':aigis, + } + res = await this.user.getData("loginByPassword",body,false) + Bot.logger.mark(`[米哈游登录] ${Bot.logger.blue(JSON.stringify(res))}`) + } + if (res.retcode == 0) { + let cookies=`stoken=${res.data.token.token}&mid=${res.data.user_info.mid}` + let cookie_token =this.user.getData("bbsGetCookie",{cookies}) + let ltoken = await this.user.getData('getLtoken', { cookies: `${cookies};stuid=${res.data.user_info.aid};` }, false) + Bot.logger.mark(`[米哈游登录] ${Bot.logger.blue(JSON.stringify(cookie_token))}`) + return { + cookie:`ltoken=${ltoken?.data?.ltoken};ltuid=${res.data.user_info.aid};cookie_token=${cookie_token?.data?.cookie_token};`, + stoken:`${cookies};stuid=${res.data.user_info.aid};` + } + } else { + await this.e.reply(`错误:${JSON.stringify(res)}`, true) + return false + } + } + async crack_geetest() { + let res =await this.user.getData("microgg", this.aigis_captcha_data, false) + Bot.logger.mark(`[米哈游登录] ${Bot.logger.blue(JSON.stringify(res))}`) + await this.e.reply(`请完成验证:${res.shorturl}`, true) + for (let n = 1; n < 60; n++) { + await utils.sleepAsync(5000) + try { + res =await this.user.getData("microggVl", this.aigis_captcha_data, false) + if (res.geetest_validate) { + return res + } + } catch (err) { + Bot.logger.error(`[米哈游登录] 错误:${Bot.logger.red(err)}`) + } + } + await this.e.reply("验证超时", true) + return false; + } +} \ No newline at end of file diff --git a/model/mys/mihoyoApi.js b/model/mys/mihoyoApi.js index cb15a09..395c6eb 100644 --- a/model/mys/mihoyoApi.js +++ b/model/mys/mihoyoApi.js @@ -12,6 +12,7 @@ import { } from '../../components/Changelog.js'; import fetch from "node-fetch" import mys from "./mysTool.js" +import crypto from "crypto"; const _path = process.cwd(); const DEVICE_ID = utils.randomString(32).toUpperCase(); const DEVICE_NAME = utils.randomString(_.random(1, 10)); @@ -71,6 +72,7 @@ export default class miHoYoApi { headers, body } = this.getUrl(type, gameBody, data) + if (!url) return false if (data.headers) { headers = { @@ -103,6 +105,7 @@ export default class miHoYoApi { Bot.logger.error(`[接口][${type}][${this.e.uid}] ${response.status} ${response.statusText}`) return false } + let res = await response.text(); // Bot.logger.mark(`[接口][${type}][${this.e.uid}] ${Date.now() - start}ms\n${res}`) if (res.startsWith('(')) { @@ -118,7 +121,9 @@ export default class miHoYoApi { Bot.logger.debug(`[米游社接口][请求参数] ${url} ${JSON.stringify(param)}`) } res.api = type - + if (type == "loginByPassword") { + res.aigis_data = JSON.parse(response.headers.get("x-rpc-aigis")) + } return res } getUrl(type, board, data) { @@ -244,11 +249,50 @@ export default class miHoYoApi { url: `${mys.pass_api}/account/auth/api/getLTokenBySToken`, query: `${data.cookies}`, }, - getByStokenV2: { - url: `${mys.pass_api}/account/ma-cn-session/app/getTokenBySToken`, - body: {}, + //用于手动过验证码,账号密码登录需要 + microgg: { + url: `https://s.microgg.cn/gt/https://validate.microgg.cn/`, + query: `gt=${data.gt}&challenge=${data.challenge}` + }, + microggVl: { + url: `https://validate.microgg.cn/`, + query: `callback=${data.challenge}` + }, + loginByPassword: { + url: "https://passport-api.mihoyo.com/account/ma-cn-passport/app/loginByPassword", + body: { + account: this.encrypt_data(data.account), + password: this.encrypt_data(data.password) + }, types: 'pass' }, + qrCodeLogin: { + url: `https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/fetch`, + body: { + app_id: mys.app_id, + device: data.device + } + }, + qrCodeQuery: { + url: `https://hk4e-sdk.mihoyo.com/hk4e_cn/combo/panda/qrcode/query`, + body: { + app_id: mys.app_id, + device: data.device, + ticket: data.ticket + } + }, + getTokenByGameToken: { + url: `https://passport-api.mihoyo.com/account/ma-cn-session/app/getTokenByGameToken`, + body: { + account_id: data.uid * 1, + game_token: data.token + }, + types: 'pass' + }, + getCookieAccountInfoByGameToken: { + url: `https://api-takumi.mihoyo.com/auth/api/getCookieAccountInfoByGameToken`, + query: `account_id=${data.uid}&game_token=${data.token}` + }, } if (!urlMap[type]) return false @@ -261,7 +305,7 @@ export default class miHoYoApi { } = urlMap[type] if (query) url += `?${query}` if (body) body = JSON.stringify(body) - let headers = this.getHeaders(board, types, sign) + let headers = this.getHeaders(board, types, sign, body, query) return { url, headers, @@ -270,7 +314,7 @@ export default class miHoYoApi { } // 签到的 headers - getHeaders(board, type = "bbs", sign) { + getHeaders(board, type = "bbs", sign, body = {}, query = '') { let header = {}; switch (type) { case "bbs": @@ -324,20 +368,26 @@ export default class miHoYoApi { break; case "cloud": header = { - "x-rpc-combo_token": this.yuntoken, //云原神签到ck - "x-rpc-client_type": "2", - "x-rpc-app_version": "1.3.0", - "x-rpc-sys_version": "11", - "x-rpc-channel": "mihoyo", - "x-rpc-device_id": this.devId, //设备Id - "x-rpc-device_name": "Xiaomi Mi 10 Pro", - "x-rpc-device_model": "Mi 10 Pro", - "x-rpc-app_id": "1953439974", - "Referer": "https://app.mihoyo.com", - "Content-Length": "0", - "Host": "api-cloudgame.mihoyo.com", - "Connection": "Keep-Alive", - "Accept-Encoding": "gzip", + // "x-rpc-combo_token": this.yuntoken, //云原神签到ck + // "x-rpc-client_type": "2", + // "x-rpc-app_version": "1.3.0", + // "x-rpc-sys_version": "11", + // "x-rpc-channel": "mihoyo", + // "x-rpc-device_id": this.devId, //设备Id + // "x-rpc-device_name": "Xiaomi Mi 10 Pro", + // "x-rpc-device_model": "Mi 10 Pro", + // "x-rpc-app_id": "1953439974", + // "Referer": "https://app.mihoyo.com", + // "Content-Length": "0", + // "Host": "api-cloudgame.mihoyo.com", + // "Connection": "Keep-Alive", + // "Accept-Encoding": "gzip", + 'Host': 'api-cloudgame.mihoyo.com', + 'Accept': '*/*', + 'Referer': 'https://app.mihoyo.com', + 'x-rpc-combo_token': this.yuntoken, + 'Accept-Encoding': 'gzip, deflate', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36 HBPC/12.1.1.301', "User-Agent": "okhttp/3.14.9" } break; @@ -390,11 +440,14 @@ export default class miHoYoApi { 'x-rpc-device_id': DEVICE_ID, 'x-rpc-app_id': "bll8iq97cem8", 'x-rpc-device_name': DEVICE_NAME, + "x-rpc-device_fp": utils.randomString(13), + "x-rpc-device_model": utils.randomString(16), 'x-rpc-app_version': mys.APP_VERSION, 'x-rpc-game_biz': 'bbs_cn', + "x-rpc-aigis": '', "Content-Type": "application/json;", "x-rpc-client_type": "2", - "DS": this.getDs2('', {}, mys.passSalt), + "DS": this.getDs2('', body, mys.passSalt), "x-rpc-sdk_version": '1.3.1.2', "User-Agent": "okhttp/4.9.3", "Referer": "cors", @@ -428,6 +481,13 @@ export default class miHoYoApi { return {} } } + encrypt_data(data) { + if (!data) return ''; + return crypto.publicEncrypt({ + key: mys.publicKey, + padding: crypto.constants.RSA_PKCS1_PADDING + }, data).toString("base64") + } //社区签到ds getDs2(q = "", b, salt) { let i = Math.floor(Date.now() / 1000) diff --git a/model/mys/mysTool.js b/model/mys/mysTool.js index 03de45e..20a39c7 100644 --- a/model/mys/mysTool.js +++ b/model/mys/mysTool.js @@ -14,6 +14,12 @@ const hk4_api = `https://hk4e-api.mihoyo.com`; const bbs_api = `https://bbs-api.mihoyo.com`; const cloud_api = `https://api-cloudgame.mihoyo.com` const pass_api=`https://passport-api.mihoyo.com` +const app_id = 4 +const publicKey = `-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDvekdPMHN3AYhm/vktJT+YJr7cI5DcsNKqdsx5DZX0gDuWFuIjzdwButrIYPNmRJ1G8ybDIF7oDW2eEpm5sMbL9zs +9ExXCdvqrn51qELbqj0XxtMTIpaCHFSI50PfPpTFV9Xt/hmyVwokoOXFlAEgCn+Q +CgGs52bFoYMtyi+xEQIDAQAB +-----END PUBLIC KEY-----` const boards = { honkai3rd: { forumid: 1, @@ -217,9 +223,9 @@ export default { osSaltWeb, web_api, os_web_api, - os_hk4_api, + os_hk4_api,app_id, hk4_api, - bbs_api, + bbs_api,publicKey, pass_api,passSalt, boards } diff --git a/resources/qrCode/index.html b/resources/qrCode/index.html new file mode 100644 index 0000000..2376cca --- /dev/null +++ b/resources/qrCode/index.html @@ -0,0 +1,29 @@ +{{extend defaultLayout}} +{{block 'css'}} + +{{/block}} +{{block 'main'}} +
t |