2
0
mirror of https://github.com/ctrlcvs/xiaoyao-cvs-plugin.git synced 2024-12-23 03:20:52 +08:00

新增指令#崩坏3签到

This commit is contained in:
ctrlcvs 2022-07-29 01:42:35 +08:00 committed by leiyilu
parent 02a86ca345
commit c6324d6323
9 changed files with 660 additions and 196 deletions

View File

@ -1,3 +1,12 @@
# 1.1.2
* 初步实现米游社签到米游币以及社区签到
* 需要引入包文件`cnpm i promise-retry `跟`cnpm i superagent`
* V3版本引入文件`pnpm i promise-retry `跟`pnpm i superagent`
* 如果出现了安装其中一个包导致另外的包出问题的请群里询问
* 具体获取cookie教程后续会完善 敬情期待~
* 需要完整的cookie内容要包含`login_ticket`跟`login_uid`不然是无法实现米游社签到的
* 自动签到逻辑目前还在梳理中.....
# 1.1.1 # 1.1.1
* 初步兼容V3版本 * 初步兼容V3版本
* 版本兼容问题V2报错的话请导入`yaml`包 指令 `cnpm i yaml` * 版本兼容问题V2报错的话请导入`yaml`包 指令 `cnpm i yaml`

View File

@ -1,12 +1,12 @@
#### 介绍 #### 介绍
# xiaoyao-cvs-plugin # xiaoyao-cvs-plugin
yunzai-bot扩展图鉴以及体力优化 yunzai-bot扩展图鉴以及体力优化;
项目仅供学习交流使用,严禁用于任何商业用途和非法行为
#### 介绍 #### 介绍
# xiaoyao-cvs-plugin
介绍 介绍
原神图鉴插件。 原神图鉴插件。
@ -31,13 +31,24 @@ git clone https://github.com/Ctrlcvs/xiaoyao-cvs-plugin.git ./plugins/xiaoyao-cv
1. 配合云崽使用, https://gitee.com/Le-niao/Yunzai-Bot 1. 配合云崽使用, https://gitee.com/Le-niao/Yunzai-Bot
2. V3版本安装报错的话请用指令引入包 2. V3版本安装报错的话请用指令引入包
``` ```
pnpm i date-format pnpm i superagent
```
```
pnpm i promise-retry
``` ```
3. V2版本安装报错指令引入包 3. V2版本安装报错指令引入包
``` ```
cnpm i yaml cnpm i yaml
``` ```
```
cnpm i superagent
```
```
cnpm i promise-retry
```
#### 命令说明 #### 命令说明
1. 发送 #图鉴更新 获取最新的图鉴记录。(必须) 1. 发送 #图鉴更新 获取最新的图鉴记录。(必须)
2. 发送 #**图鉴 进行触发,例如发送 #刻晴图鉴,即可返回对应的图片信息。 2. 发送 #**图鉴 进行触发,例如发送 #刻晴图鉴,即可返回对应的图片信息。

View File

@ -26,7 +26,6 @@ let path_img = ["background_image", "/icon/bg"];
let tempDataUrl = `${_path}/plugins/xiaoyao-cvs-plugin/data/NoteTemp` let tempDataUrl = `${_path}/plugins/xiaoyao-cvs-plugin/data/NoteTemp`
let tempData = {}; let tempData = {};
init() init()
function init() { function init() {
Data.createDir("", tempDataUrl, false); Data.createDir("", tempDataUrl, false);
tempData = Data.readJSON(tempDataUrl, "tempData") tempData = Data.readJSON(tempDataUrl, "tempData")

View File

@ -19,15 +19,19 @@ import {
import { import {
currentVersion currentVersion
} from "../components/Changelog.js"; } from "../components/Changelog.js";
import {
rule as signRule,
sign,mysSign
} from "./sign.js"
export { export {
updateRes, updateRes,
updateMiaoPlugin, updateMiaoPlugin,sign,
versionInfo, versionInfo,
Note_appoint,pokeNote, Note_appoint,pokeNote,
sysCfg, sysCfg,
help,DailyNoteTask, help,DailyNoteTask,
AtlasAlias, AtlasAlias,
Note Note,mysSign
}; };
let rule = { let rule = {
@ -55,7 +59,7 @@ let rule = {
reg: "#poke#", reg: "#poke#",
describe: "体力", describe: "体力",
}, },
...signRule,
...adminRule ...adminRule
}; };

181
apps/sign.js Normal file
View File

@ -0,0 +1,181 @@
import MihoYoApi from "../model/mys/mihoyo-api.js"
import utils from '../model/mys/utils.js';
import promiseRetry from 'promise-retry';
import {
Cfg,
Data
} from "../components/index.js";
import moment from 'moment';
import MysInfo from '../model/mys/mysInfo.js'
import {
isV3
} from '../components/Changelog.js';
import gsCfg from '../model/gsCfg.js'
import { fileFrom } from "node-fetch";
export const rule = {
mysSign: {
reg: "^#*(米游社|mys|社区)(原神|崩坏3|崩坏2|未定事件簿|大别野|崩坏星穹铁道|绝区零|全部)签到$",
describe: "米游社米游币签到(理论上会签到全部所以区分开了)"
},
sign: {
reg: "^#*(原神|崩坏3|崩坏2|未定事件簿|大别野|崩坏星穹铁道|绝区零)签到$",
describe: "米社规则签到"
}
};
const _path = process.cwd();
let START = moment().unix();
const TODAY_DATE = moment().format('YYYY-MM-DD');
const RETRY_OPTIONS = {
retries: 3,
minTimeout: 5000,
maxTimeout: 10000
};
export async function sign(e) {
if (!(await cookie(e))) {
return true;
}
START = moment().unix();
let miHoYoApi = new MihoYoApi(e);
let resultMessage="";
let msg = e.msg.replace(/#|签到|井|米游社|mys|社区/g, "");
let ForumData = await getDataList(msg);
for (let forum of ForumData) {
resultMessage += `**${forum.name}**\n`
try {
// 1 BBS Sign
let resObj = await promiseRetry((retry, number) => {
Bot.logger.info(`开始签到: [${forum.name}] 尝试次数: ${number}`);
return miHoYoApi.forumSign(forum.forumId).catch((e) => {
Bot.logger.error(`${forum.name} 签到失败: [${e.message}] 尝试次数: ${number}`);
return retry(e);
});
}, RETRY_OPTIONS);
Bot.logger.info(`${forum.name} 签到结果: [${resObj.message}]`);
resultMessage += `签到: [${resObj.message}]\n`;
} catch (e) {
Bot.logger.error(`${forum.name} 签到失败 [${e.message}]`);
resultMessage += `签到失败: [${e.message}]\n`;
}
await utils.randomSleepAsync();
}
await replyMsg(e,resultMessage);
return true
}
export async function mysSign(e) {
if (!(await cookie(e))) {
return true;
}
START = moment().unix();
let miHoYoApi = new MihoYoApi(e);
let resultMessage="";
// Execute task
let msg = e.msg.replace(/#|签到|井|米游社|mys|社区/g, "");
let ForumData = await getDataList(msg);
for (let forum of ForumData) {
resultMessage += `\n**${forum.name}**\n`
try {
// 2 BBS list post
let resObj = await promiseRetry((retry, number) => {
Bot.logger.info(`读取帖子列表: [${forum.name}] 尝试次数: ${number}`);
return miHoYoApi.forumPostList(forum.forumId).catch((e) => {
Bot.logger.error(`${forum.name} 读取帖子列表失败: [${e.message}] 尝试次数: ${number}`);
return retry(e);
});
}, RETRY_OPTIONS);
Bot.logger.info(`${forum.name} 读取列表成功 [${resObj.message}],读取到 [${resObj.data.list.length}] 条记录`);
let postList = resObj.data.list;
for (let post of postList) {
post = post.post;
// 2.1 BBS read post
let resObj = await promiseRetry((retry, number) => {
Bot.logger.info(`读取帖子: [${post.subject}] 尝试次数: ${number}`);
return miHoYoApi.forumPostDetail(post['post_id']).catch((e) => {
Bot.logger.error(`${forum.name} 读取帖子失败: [${e.message}] 尝试次数: ${number}`);
return retry(e);
});
}, RETRY_OPTIONS);
Bot.logger.info(`${forum.name} [${post.subject}] 读取成功 [${resObj.message}]`);
await utils.randomSleepAsync();
// 2.2 BBS vote post
resObj = await promiseRetry((retry, number) => {
Bot.logger.info(`点赞帖子: [${post.subject}] 尝试次数: ${number}`);
return miHoYoApi.forumPostVote(post['post_id']).catch((e) => {
Bot.logger.error(`${forum.name} 点赞帖子失败: [${e.message}] 尝试次数: ${number}`);
return retry(e);
});
}, RETRY_OPTIONS);
Bot.logger.info(`${forum.name} [${post.subject}] 点赞成功 [${resObj.message}]`);
await utils.randomSleepAsync();
}
// 2.3 BBS share post
let sharePost = postList[0].post;
resObj = await promiseRetry((retry, number) => {
Bot.logger.info(`分享帖子: [${sharePost.subject}] 尝试次数: ${number}`);
return miHoYoApi.forumPostShare(sharePost['post_id']).catch((e) => {
Bot.logger.error(`${forum.name} 分享帖子失败: [${e.message}] 尝试次数: ${number}`);
return retry(e);
});
}, RETRY_OPTIONS);
} catch (e) {
Bot.logger.error(`${forum.name} 读帖点赞分享失败 [${e.message}]`);
resultMessage += `读帖点赞分享: 失败 [${e.message}]\n`;
}
resultMessage += `读帖点赞分享: 成功\n`;
await utils.randomSleepAsync();
}
await replyMsg(e,resultMessage);
return true
}
async function replyMsg(e,resultMessage){
const END = moment().unix();
Bot.logger.info(`运行结束, 用时 ${END - START}`);
resultMessage += `\n用时 ${END - START}`;
e.reply(resultMessage);
}
async function getDataList(name){
let ForumData = Data.readJSON(`${_path}/plugins/xiaoyao-cvs-plugin/defSet/json`, "mys")
for(let item of ForumData){
if(item.name=name){ //循环结束未找到的时候返回原数组签到全部
return [item]
}
}
return ForumData;
}
async function cookie(e) {
let cookie, uid;
let miHoYoApi = new MihoYoApi(e);
if (isV3) {
// console.log(e)
let skuid = await gsCfg.getBingCookie(e.user_id);
cookie = skuid.ck;
uid = skuid.item;
} else {
if (NoteCookie[e.user_id]) {
cookie = NoteCookie[e.user_id].cookie;
uid = NoteCookie[e.user_id].uid;
} else if (BotConfig.dailyNote && BotConfig.dailyNote[e.user_id]) {
cookie = BotConfig.dailyNote[e.user_id].cookie;
uid = BotConfig.dailyNote[e.user_id].uid;
}
}
e.uid = uid;
if (!cookie.includes("login_ticket")) {
e.reply("米游社登录cookie不完整请前往米游社通行证处重新获取cookie~\ncookies必须包含login_ticket")
return false;
}
let flot = (await miHoYoApi.stoken(cookie, e));
if (!flot) {
e.reply("登录失效请重新登录获取cookie发送机器人~")
return false;
}
return true;
}

44
defSet/json/mys.json Normal file
View File

@ -0,0 +1,44 @@
[
{
"id": "1",
"forumId": "1",
"name": "崩坏3",
"url": "https://bbs.mihoyo.com/bh3/"
},
{
"id": "2",
"forumId": "26",
"name": "原神",
"url": "https://bbs.mihoyo.com/ys/"
},
{
"id": "3",
"forumId": "30",
"name": "崩坏2",
"url": "https://bbs.mihoyo.com/bh2/"
},
{
"id": "4",
"forumId": "37",
"name": "未定事件簿",
"url": "https://bbs.mihoyo.com/wd/"
},
{
"id": "5",
"forumId": "34",
"name": "大别野",
"url": "https://bbs.mihoyo.com/dby/"
},
{
"id": "6",
"forumId": "52",
"name": "崩坏星穹铁道",
"url": "https://bbs.mihoyo.com/sr/"
},
{
"id": "7",
"forumId": "57",
"name": "绝区零",
"url": "https://bbs.mihoyo.com/zzz/"
}
]

View File

@ -1,233 +1,262 @@
import YAML from 'yaml' import YAML from 'yaml'
import chokidar from 'chokidar' import chokidar from 'chokidar'
import fs from 'node:fs' import fs from 'node:fs'
import { promisify } from 'node:util' import {
promisify
} from 'node:util'
import lodash from 'lodash' import lodash from 'lodash'
const plugin="xiaoyao-cvs-plugin" const plugin = "xiaoyao-cvs-plugin"
/** 配置文件 */ /** 配置文件 */
class GsCfg { class GsCfg {
constructor () { constructor() {
/** 默认设置 */ /** 默认设置 */
this.defSetPath = `./plugins/${plugin}/defSet/` this.defSetPath = `./plugins/${plugin}/defSet/`
this.defSet = {} this.defSet = {}
/** 用户设置 */ /** 用户设置 */
this.configPath = `./plugins/${plugin}/config/` this.configPath = `./plugins/${plugin}/config/`
this.config = {} this.config = {}
/** 监听文件 */ /** 监听文件 */
this.watcher = { config: {}, defSet: {} } this.watcher = {
} config: {},
defSet: {}
}
}
/** /**
* @param app 功能 * @param app 功能
* @param name 配置文件名称 * @param name 配置文件名称
*/ */
getdefSet (app, name) { getdefSet(app, name) {
return this.getYaml(app, name, 'defSet') return this.getYaml(app, name, 'defSet')
} }
/** 用户配置 */ /** 用户配置 */
getConfig (app, name) { getConfig(app, name) {
let ignore = ['mys.pubCk', 'gacha.set'] let ignore = ['mys.pubCk', 'gacha.set']
if (ignore.includes(`${app}.${name}`)) { if (ignore.includes(`${app}.${name}`)) {
return this.getYaml(app, name, 'config') return this.getYaml(app, name, 'config')
} }
return { ...this.getdefSet(app, name), ...this.getYaml(app, name, 'config') } return {
} ...this.getdefSet(app, name),
...this.getYaml(app, name, 'config')
}
}
/** /**
* 获取配置yaml * 获取配置yaml
* @param app 功能 * @param app 功能
* @param name 名称 * @param name 名称
* @param type 默认跑配置-defSet用户配置-config * @param type 默认跑配置-defSet用户配置-config
*/ */
getYaml (app, name, type) { getYaml(app, name, type) {
let file = this.getFilePath(app, name, type) let file = this.getFilePath(app, name, type)
let key = `${app}.${name}` let key = `${app}.${name}`
if (this[type][key]) return this[type][key] if (this[type][key]) return this[type][key]
this[type][key] = YAML.parse( this[type][key] = YAML.parse(
fs.readFileSync(file, 'utf8') fs.readFileSync(file, 'utf8')
) )
this.watch(file, app, name, type) this.watch(file, app, name, type)
return this[type][key] return this[type][key]
} }
getFilePath (app, name, type) { getFilePath(app, name, type) {
if (type == 'defSet') return `${this.defSetPath}${app}/${name}.yaml` if (type == 'defSet') return `${this.defSetPath}${app}/${name}.yaml`
else return `${this.configPath}${app}.${name}.yaml` else return `${this.configPath}${app}.${name}.yaml`
} }
/** 监听配置文件 */ /** 监听配置文件 */
watch (file, app, name, type = 'defSet') { watch(file, app, name, type = 'defSet') {
let key = `${app}.${name}` let key = `${app}.${name}`
if (this.watcher[type][key]) return if (this.watcher[type][key]) return
const watcher = chokidar.watch(file) const watcher = chokidar.watch(file)
watcher.on('change', path => { watcher.on('change', path => {
delete this[type][key] delete this[type][key]
logger.mark(`[修改配置文件][${type}][${app}][${name}]`) logger.mark(`[修改配置文件][${type}][${app}][${name}]`)
if (this[`change_${app}${name}`]) { if (this[`change_${app}${name}`]) {
this[`change_${app}${name}`]() this[`change_${app}${name}`]()
} }
}) })
this.watcher[type][key] = watcher this.watcher[type][key] = watcher
} }
get element () { get element() {
return { ...this.getdefSet('element', 'role'), ...this.getdefSet('element', 'weapon') } return {
} ...this.getdefSet('element', 'role'),
...this.getdefSet('element', 'weapon')
}
}
/** 读取用户绑定的ck */ /** 读取用户绑定的ck */
async getBingCk () { async getBingCk() {
let ck = {} let ck = {}
let ckQQ = {} let ckQQ = {}
let dir = './data/MysCookie/' let dir = './data/MysCookie/'
let files = fs.readdirSync(dir).filter(file => file.endsWith('.yaml')) let files = fs.readdirSync(dir).filter(file => file.endsWith('.yaml'))
const readFile = promisify(fs.readFile) const readFile = promisify(fs.readFile)
let promises = [] let promises = []
files.forEach((v) => promises.push(readFile(`${dir}${v}`, 'utf8'))) files.forEach((v) => promises.push(readFile(`${dir}${v}`, 'utf8')))
const res = await Promise.all(promises) const res = await Promise.all(promises)
res.forEach((v) => { res.forEach((v) => {
let tmp = YAML.parse(v) let tmp = YAML.parse(v)
lodash.forEach(tmp, (v, i) => { lodash.forEach(tmp, (v, i) => {
ck[String(i)] = v ck[String(i)] = v
if (v.isMain && !ckQQ[String(v.qq)]) { if (v.isMain && !ckQQ[String(v.qq)]) {
ckQQ[String(v.qq)] = v ckQQ[String(v.qq)] = v
} }
}) })
}) })
return { ck, ckQQ } return {
} ck,
ckQQ
}
}
getBingCkSingle (userId) { getBingCkSingle(userId) {
let file = `./data/MysCookie/${userId}.yaml` let file = `./data/MysCookie/${userId}.yaml`
try { try {
let ck = fs.readFileSync(file, 'utf-8') let ck = fs.readFileSync(file, 'utf-8')
ck = YAML.parse(ck) ck = YAML.parse(ck)
return ck return ck
} catch (error) { } catch (error) {
return {} return {}
} }
} }
getBingCookie(userId) {
let file = `./data/MysCookie/${userId}.yaml`
try {
let ck = fs.readFileSync(file, 'utf-8')
ck = YAML.parse(ck)
for(let item in ck){
ck=ck[item].ck
return {ck,item};
}
} catch (error) {
return {}
}
}
saveBingCk(userId, data) {
let file = `./data/MysCookie/${userId}.yaml`
if (lodash.isEmpty(data)) {
fs.existsSync(file) && fs.unlinkSync(file)
} else {
let yaml = YAML.stringify(data)
fs.writeFileSync(file, yaml, 'utf8')
}
}
saveBingCk (userId, data) { /**
let file = `./data/MysCookie/${userId}.yaml` * 原神角色id转换角色名字
if (lodash.isEmpty(data)) { */
fs.existsSync(file) && fs.unlinkSync(file) roleIdToName(id) {
} else { let name = this.getdefSet('role', 'name')
let yaml = YAML.stringify(data) if (name[id]) {
fs.writeFileSync(file, yaml, 'utf8') return name[id][0]
} }
}
/** return ''
* 原神角色id转换角色名字 }
*/
roleIdToName (id) {
let name = this.getdefSet('role', 'name')
if (name[id]) {
return name[id][0]
}
return '' /** 原神角色别名转id */
} roleNameToID(keyword) {
if (!this.nameID) {
this.nameID = new Map()
let nameArr = this.getdefSet('role', 'name')
for (let i in nameArr) {
for (let val of nameArr[i]) {
this.nameID.set(val, i)
}
}
}
if (!isNaN(keyword)) keyword = Number(keyword)
let roelId = this.nameID.get(keyword)
return roelId || false
}
/** 原神角色别名转id */ /**
roleNameToID (keyword) { * 原神角色武器长名称缩写
if (!this.nameID) { * @param name 名称
this.nameID = new Map() * @param isWeapon 是否武器
let nameArr = this.getdefSet('role', 'name') */
for (let i in nameArr) { shortName(name, isWeapon = false) {
for (let val of nameArr[i]) { let other = {}
this.nameID.set(val, i) if (isWeapon) {
} other = this.getdefSet('weapon', 'other')
} } else {
} other = this.getdefSet('role', 'other')
if (!isNaN(keyword)) keyword = Number(keyword) }
let roelId = this.nameID.get(keyword) return other.sortName[name] ?? name
return roelId || false }
}
/** /** 公共配置ck文件修改hook */
* 原神角色武器长名称缩写 async change_myspubCk() {
* @param name 名称 let MysInfo = await import('./mys/mysInfo.js').default
* @param isWeapon 是否武器 await new MysInfo().addPubCk()
*/ }
shortName (name, isWeapon = false) {
let other = {}
if (isWeapon) {
other = this.getdefSet('weapon', 'other')
} else {
other = this.getdefSet('role', 'other')
}
return other.sortName[name] ?? name
}
/** 公共配置ck文件修改hook */ getGachaSet(groupId = '') {
async change_myspubCk () { let config = this.getYaml('gacha', 'set', 'config')
let MysInfo = await import('./mys/mysInfo.js').default let def = config.default
await new MysInfo().addPubCk() if (config[groupId]) {
} return {
...def,
...config[groupId]
}
}
return def
}
getGachaSet (groupId = '') { getMsgUid(msg) {
let config = this.getYaml('gacha', 'set', 'config') let ret = /[1|2|5][0-9]{8}/g.exec(msg)
let def = config.default if (!ret) return false
if (config[groupId]) { return ret[0]
return { ...def, ...config[groupId] } }
}
return def
}
getMsgUid (msg) { /**
let ret = /[1|2|5][0-9]{8}/g.exec(msg) * 获取消息内原神角色名称uid
if (!ret) return false * @param msg 判断消息
return ret[0] * @param filterMsg 过滤消息
} * @return roleId 角色id
* @return name 角色名称
* @return alias 当前别名
* @return uid 游戏uid
*/
getRole(msg, filterMsg = '') {
let alias = msg.replace(/#|老婆|老公|[1|2|5][0-9]{8}/g, '').trim()
if (filterMsg) {
alias = alias.replace(new RegExp(filterMsg, 'g'), '').trim()
}
/** /** 判断是否命中别名 */
* 获取消息内原神角色名称uid let roleId = this.roleNameToID(alias)
* @param msg 判断消息 if (!roleId) return false
* @param filterMsg 过滤消息 /** 获取uid */
* @return roleId 角色id let uid = this.getMsgUid(msg) || ''
* @return name 角色名称
* @return alias 当前别名
* @return uid 游戏uid
*/
getRole (msg, filterMsg = '') {
let alias = msg.replace(/#|老婆|老公|[1|2|5][0-9]{8}/g, '').trim()
if (filterMsg) {
alias = alias.replace(new RegExp(filterMsg, 'g'), '').trim()
}
/** 判断是否命中别名 */ return {
let roleId = this.roleNameToID(alias) roleId,
if (!roleId) return false uid,
/** 获取uid */ alias,
let uid = this.getMsgUid(msg) || '' name: this.roleIdToName(roleId)
}
return { }
roleId,
uid,
alias,
name: this.roleIdToName(roleId)
}
}
} }
export default new GsCfg() export default new GsCfg()

158
model/mys/mihoyo-api.js Normal file
View File

@ -0,0 +1,158 @@
import utils from './utils.js';
import md5 from 'md5';
import _ from 'lodash';
import superagent from 'superagent';
import fs from "fs";
import YAML from 'yaml'
import {Data} from "../../components/index.js";
import fetch from "node-fetch"
const APP_VERSION = "2.2.0";
const DEVICE_ID = utils.randomString(32).toUpperCase();
const DEVICE_NAME = utils.randomString(_.random(1, 10));
const _path = process.cwd();
let YamlDataUrl = `${_path}/plugins/xiaoyao-cvs-plugin/data/yaml`
export default class MihoYoApi {
constructor(e) {
if (e) {
this.e = e
this.userId = String(e.user_id)
}
Data.createDir("", YamlDataUrl, false);
}
async forumSign(forumId) {
const url = `https://api-takumi.mihoyo.com/apihub/sapi/signIn?gids=${forumId}`;
let res = await superagent.post(url).set(this._getHeader()).timeout(10000);
let resObj = JSON.parse(res.text);
// Bot.logger.mark(`ForumSign: ${res.text}`);
return resObj;
}
async forumPostList(forumId) {
const url =
`https://api-takumi.mihoyo.com/post/api/getForumPostList?forum_id=${forumId}&is_good=false&is_hot=false&page_size=20&sort_type=1`;
let res = await superagent.get(url).set(this._getHeader()).timeout(10000);
let resObj = JSON.parse(res.text);
// logger.mark(`ForumList: ${res.text}`)
return resObj;
}
async forumPostDetail(postId) {
const url = `https://api-takumi.mihoyo.com/post/api/getPostFull?post_id=${postId}`;
let res = await superagent.get(url).set(this._getHeader()).timeout(10000);
let resObj = JSON.parse(res.text);
// logger.mark(`ForumDetail: ${res.text}`)
return resObj;
}
async forumPostShare(postId) {
const url = `https://api-takumi.mihoyo.com/apihub/api/getShareConf?entity_id=${postId}&entity_type=1`;
let res = await superagent.get(url).set(this._getHeader()).timeout(10000);
let resObj = JSON.parse(res.text);
// Bot.logger.mark(`ForumShare: ${res.text}`)
return resObj;
}
async forumPostVote(postId) {
const url = `https://api-takumi.mihoyo.com/apihub/sapi/upvotePost`;
const upvotePostData = {
"post_id": postId,
"is_cancel": false
}
let res = await superagent.post(url).set(this._getHeader()).send(JSON.stringify(upvotePostData));
let resObj = JSON.parse(res.text);
// Bot.logger.mark(`ForumVote: ${res.text}`)
return resObj;
}
async stoken(cookie, e) {
this.e=e;
if(this.getStoken(e.user_id)){
return true;
}
const map = this.getCookieMap(cookie);
const loginTicket = map.get("login_ticket");
const loginUid = map.get("login_uid");
const url = "https://api-takumi.mihoyo.com/auth/api/getMultiTokenByLoginTicket?login_ticket=" +
loginTicket + "&token_types=3&uid=" + loginUid;
fetch(url, {
"headers": {
"x-rpc-device_id": "zxcvbnmasadfghjk123456",
"Content-Type": "application/json;charset=UTF-8",
"x-rpc-client_type": "",
"x-rpc-app_version": "",
"DS": "",
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) miHoYoBBS/%s",
"Referer": "cors",
"Accept-Encoding": "gzip, deflate, br",
"x-rpc-channel": "appstore",
},
"method": "GET"
}).then(
function(response) {
if (response.status !== 200) {
return false;
}
response.json().then(function(data) {
// console.log(data);
let datalist = {
stuid: map.get("account_id"),
stoken: data.data.list[0].token,
ltoken: data.data.list[1].token,
uid: e.uid
}
let yamlStr = YAML.stringify(datalist);
fs.writeFileSync(`${YamlDataUrl}/${e.user_id}.yaml`, yamlStr, 'utf8');
return true;
});
}
).catch(function(err) {
return false;
});
return true;
}
_getHeader() {
const randomStr = utils.randomString(6);
const timestamp = Math.floor(Date.now() / 1000)
let data=this.getStoken(this.e.user_id);
// console.log(data)
// iOS sign
let sign = md5(`salt=b253c83ab2609b1b600eddfe974df47b&t=${timestamp}&r=${randomStr}`);
let cookie =`stuid=${data.stuid};stoken=${data.stoken};ltoken=${data.ltoken};`;
return {
'Cookie': cookie,
'Content-Type': 'application/json',
'User-Agent': 'Hyperion/67 CFNetwork/1128.0.1 Darwin/19.6.0',
'Referer': 'https://app.mihoyo.com',
'x-rpc-channel': 'appstore',
'x-rpc-device_id': DEVICE_ID,
'x-rpc-app_version': APP_VERSION,
'x-rpc-device_model': 'iPhone11,8',
'x-rpc-device_name': DEVICE_NAME,
'x-rpc-client_type': '1', // 1 - iOS, 2 - Android, 4 - Web
'DS': `${timestamp},${randomStr},${sign}`
// 'DS': `1602569298,k0xfEh,07f4545f5d88eac59cb1257aef74a570`
}
}
getCookieMap(cookie) {
let cookiePattern = /^(\S+)=(\S+)$/;
let cookieArray = cookie.split("; ");
let cookieMap = new Map();
for (let item of cookieArray) {
let entry = cookiePattern.exec(item);
cookieMap.set(entry[1], entry[2]);
}
return cookieMap;
}
getStoken(userId) {
let file = `${YamlDataUrl}/${userId}.yaml`
try {
let ck = fs.readFileSync(file, 'utf-8')
ck = YAML.parse(ck)
return ck
} catch (error) {
return {}
}
}
}

29
model/mys/utils.js Normal file
View File

@ -0,0 +1,29 @@
import _ from 'lodash';
export async function sleepAsync(sleepms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, sleepms)
});
}
export async function randomSleepAsync(){
let sleep = 2 * 1000 + _.random(3 * 1000);
await sleepAsync(sleep);
}
export function randomString(length){
let randomStr = '';
for (let i = 0; i < length; i++) {
randomStr += _.sample('abcdefghijklmnopqrstuvwxyz0123456789');
}
return randomStr;
}
export default {
sleepAsync,
randomSleepAsync,
randomString
}