Miao-Yunzai/plugins/genshin/model/payLogData.js

336 lines
9.1 KiB
JavaScript
Raw 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.

import puppeteer from '../../../lib/puppeteer/puppeteer.js'
import fetch from 'node-fetch'
import moment from 'moment'
import lodash from 'lodash'
import fs from 'fs'
import base from './base.js'
if (!fs.existsSync('./data/payLog/')) {
fs.mkdirSync('./data/payLog/')
}
export class PayData {
constructor (authKey = '') {
this.#authkey = encodeURIComponent(authKey)
}
#genShinId = ''
#oringinData = []
#authkey = ''
/** 获取原始支付数据 */
async getOringinalData (id = '') {
let res = await fetch(this.getUrl() + id, this.headers)
let ret = await res.json()
let check = this.checkResult(ret)
if (check?.errorMsg) return check
let list = ret?.data?.list
if (!Array.isArray(list)) {
console.error(ret)
return { errorMsg: '获取失败,错误码:' + ret?.retcode }
}
if (list.length === 20) {
this.#oringinData.push(...list)
await this.getOringinalData(list[19].id)
return true
} else {
this.#oringinData.push(...list)
return true
}
}
/** 获取大月卡数据 */
async getPrimogemLog (id = '') {
let res = await fetch(this.getUrl('getPrimogemLog') + id, this.headers)
let ret = await res.json()
let check = this.checkResult(ret)
if (check?.errorMsg) return check
let list = ret?.data?.list
if (!Array.isArray(list)) {
console.error(ret)
return { errorMsg: '获取失败,错误码:' + ret?.retcode }
}
if (list.length === 20) {
list.forEach(v => {
if (v.add_num === '680') this.#oringinData.push(v)
})
await this.getPrimogemLog(list[19].id)
return true
} else {
list.forEach(v => {
if (v.add_num === '680') this.#oringinData.push(v)
})
return true
}
}
async getUserInfo() {
let res = await fetch(this.getUrl('getUserInfo'), this.headers)
let ret = await res.json()
let check = this.checkResult(ret)
if (check?.errorMsg) return check
let data = ret?.data
if (data?.uid) {
return data
}
return {errorMsg: '获取失败,可能是链接已过期或不正确'}
}
checkResult(ret) {
if (ret?.retcode === -101 || ret?.retcode === -100) {
return ret.retcode === -101 ? {errorMsg: '您的链接过期,请重新获取'} : {errorMsg: '链接不正确,请重新获取'}
}
if (/unknown auth appid/.test(ret?.message)) {
return {errorMsg: '抽卡或其他链接现已无法获取充值记录,请发送客服页面的链接!'}
}
return {errorMsg: ''}
}
/** 对原始数据进行筛选,组合 */
async filtrateData () {
// 由于新接口不返回uid了所以先查询出用户信息
const userInfo = await this.getUserInfo()
if (userInfo?.errorMsg) return userInfo
this.#genShinId = userInfo.uid
// 获取数据
let isSucceed = await this.getOringinalData()
// 判断数据是否获取成功
if (isSucceed?.errorMsg) return isSucceed
await this.getPrimogemLog()
// 判断零氪党的情况
if (this.#oringinData.length === 0) {
return {errorMsg: '未获取到您的任何充值数据'}
}
// 将原始数据按id排序
this.#oringinData = this.#oringinData.sort((a, b) => {
let val1 = Number(a.id)
let val2 = Number(b.id)
if (val2 > val1) {
return -1
} else {
return 1
}
})
// 单双倍结晶数
const price = [680, 300, 8080, 3880, 2240, 1090, 330, 60]
const doublePrice = [0, 0, 12960, 6560, 3960, 1960, 600, 120]
let month = 0
let sum = 0
let i = -1
let listIndex = 0
let list = []
for (let index = 0; index < this.#oringinData.length; index++) {
// 如果小于零则返回
let num = Number(this.#oringinData[index].add_num)
if (num < 0) continue
// 获取月份
// let thisMonth = ++moment(this.#oringinData[index].time).toArray()[1]
// 新接口改名为datetime
let thisMonth = ++moment(this.#oringinData[index].datetime).toArray()[1]
if (thisMonth !== month) {
i++
month = thisMonth
list[listIndex++] = {
month: thisMonth + '月',
payNum: [0, 0, 0, 0, 0, 0, 0, 0]
}
} else if (!i) {
list[i] = {
month: thisMonth + '月',
payNum: [0, 0, 0, 0, 0, 0, 0, 0]
}
}
for (let index = 0; index < 8; index++) {
if (num === price[index] || num === doublePrice[index]) {
list[i].payNum[index]++
if (num !== 680) sum += num
break
}
}
}
return {
uid: this.#genShinId,
crystal: sum,
monthData: list
}
}
headers = {
headers: {
accept: 'application/json, text/plain, */*',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-site'
},
referrer: 'https://webstatic.mihoyo.com/',
referrerPolicy: 'strict-origin-when-cross-origin',
method: 'GET',
mode: 'cors',
credentials: 'include'
}
/**
* 获取url
* @param api 原石 getPrimogemLog // 结晶 getCrystalLog // 获取用户信息 getUserInfo
* @returns {string}
*/
getUrl(api = 'getCrystalLog') {
const baseUrl = 'https://hk4e-api.mihoyo.com/common/hk4e_self_help_query/User'
const isUserInfo = api === 'getUserInfo', isCrystalLog = api === 'getCrystalLog'
const url = isUserInfo ? '/GetUserInfo' : isCrystalLog ? '/GetCrystalLog' : '/GetPrimogemLog'
let params = ''
params += '?selfquery_type=1'
params += '&sign_type=2'
params += '&auth_appid=csc'
params += '&authkey_ver=1'
params += '&game_biz=hk4e_cn'
params += '&win_direction=portrait'
params += '&bbs_auth_required=true'
params += '&bbs_game_role_required=hk4e_cn'
params += '&app_client=bbs'
params += '&lang=zh-cn'
params += '&csc_authkey_required=true'
params += '&authkey=' + this.#authkey
params += '&page_id=1'
// 此条件限定只查询获取的
if (!isUserInfo) {
params += '&add_type=produce'
params += '&size=20'
// endId外部拼接
params += '&end_id='
}
return baseUrl + url + params
// 老API
// let type = api === 'getCrystalLog' ? 3 : 1
// return `https://hk4e-api.mihoyo.com/ysulog/api/${api}?selfquery_type=${type}&lang=zh-cn&sign_type=2&auth_appid=csc&authkey_ver=1&authkey=${this.#authkey}&game_biz=hk4e_cn&app_client=bbs&type=${type}&size=20&end_id=`
}
}
export class HtmlData extends base {
/**
* @param data 数据
* @param data.monthData 月份数据
* @param data.crystal 总结晶数
*/
constructor (data = {}) {
super()
this.monthData = data.monthData
this.crystal = data.crystal
this.uid = data.uid
this.model = 'payLog'
}
crystal = 0
uid = ''
monthData = []
// 价格
price = [68, 30, 648, 328, 198, 98, 30, 6]
/** 柱形图数据 */
getBarData () {
return this.monthData.map(v => {
return {
type: v.month,
sales: v.payNum.reduce((sum, val, index) => sum + val * this.price[index], 0)
}
})
}
/** 顶部数据 */
getTopData (crystal = 0) {
const maxMonth = this.maxcConsumption()
const sum = this.sumConsumption()
return [
{
title: '总消费',
value: '¥' + this.getBarData().reduce((sum, val) => sum + val.sales, 0)
},
{
title: '总结晶',
value: this.crystal
},
{
title: '消费最多',
value: maxMonth.type
},
{
title: maxMonth.type + '消费',
value: '¥' + maxMonth.sales
},
...sum
]
}
/** 饼图数据 */
getPieData () {
const data = this.sumConsumption()
let pieData = []
data.forEach((val, index) => {
let value = val.value * this.price[index]
if (value) {
pieData.push({
value,
name: val.title
})
}
})
return pieData
}
/** 消费最多月 */
maxcConsumption () {
return this.getBarData().sort((a, b) => {
if (a.sales < b.sales) {
return 1
} else {
return -1
}
})[0]
}
/** 每种消费的总数 */
sumConsumption () {
let sum = {
小月卡: 0,
大月卡: 0,
648: 0,
328: 0,
198: 0,
98: 0,
30: 0,
6: 0
}
// 循环sum,按照月份统计各个充值的类别总数
let k = Object.keys(sum).reverse()
this.monthData.forEach(val => {
val.payNum.forEach((v, i) => {
sum[k[i]] += v
})
})
// 返回title,value对象'648':123456
let value = Object.values(sum).reverse()
return k.map((val, index) => {
return {
title: val,
value: value[index]
}
})
}
}
export async function renderImg (data) {
const htmlData = new HtmlData(data)
const imgDatas = {
...htmlData.screenData,
topData: htmlData.getTopData(),
barData: JSON.stringify(htmlData.getBarData()),
pieData: JSON.stringify(htmlData.getPieData()),
saveId: htmlData.uid,
uid: htmlData.uid
}
let img = await puppeteer.screenshot('payLog', imgDatas)
return img
}