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
* 初步兼容V3版本
* 版本兼容问题V2报错的话请导入`yaml`包 指令 `cnpm i yaml`

View File

@ -1,12 +1,12 @@
#### 介绍
# 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
2. V3版本安装报错的话请用指令引入包
```
pnpm i date-format
pnpm i superagent
```
```
pnpm i promise-retry
```
3. V2版本安装报错指令引入包
```
cnpm i yaml
```
```
cnpm i superagent
```
```
cnpm i promise-retry
```
#### 命令说明
1. 发送 #图鉴更新 获取最新的图鉴记录。(必须)
2. 发送 #**图鉴 进行触发,例如发送 #刻晴图鉴,即可返回对应的图片信息。

View File

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

View File

@ -19,15 +19,19 @@ import {
import {
currentVersion
} from "../components/Changelog.js";
import {
rule as signRule,
sign,mysSign
} from "./sign.js"
export {
updateRes,
updateMiaoPlugin,
updateMiaoPlugin,sign,
versionInfo,
Note_appoint,pokeNote,
sysCfg,
help,DailyNoteTask,
AtlasAlias,
Note
Note,mysSign
};
let rule = {
@ -55,7 +59,7 @@ let rule = {
reg: "#poke#",
describe: "体力",
},
...signRule,
...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,7 +1,9 @@
import YAML from 'yaml'
import chokidar from 'chokidar'
import fs from 'node:fs'
import { promisify } from 'node:util'
import {
promisify
} from 'node:util'
import lodash from 'lodash'
const plugin = "xiaoyao-cvs-plugin"
@ -17,7 +19,10 @@ class GsCfg {
this.config = {}
/** 监听文件 */
this.watcher = { config: {}, defSet: {} }
this.watcher = {
config: {},
defSet: {}
}
}
/**
@ -36,7 +41,10 @@ class GsCfg {
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')
}
}
/**
@ -84,7 +92,10 @@ class GsCfg {
}
get element() {
return { ...this.getdefSet('element', 'role'), ...this.getdefSet('element', 'weapon') }
return {
...this.getdefSet('element', 'role'),
...this.getdefSet('element', 'weapon')
}
}
/** 读取用户绑定的ck */
@ -112,7 +123,10 @@ class GsCfg {
})
})
return { ck, ckQQ }
return {
ck,
ckQQ
}
}
getBingCkSingle(userId) {
@ -125,7 +139,19 @@ class GsCfg {
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)) {
@ -189,7 +215,10 @@ class GsCfg {
let config = this.getYaml('gacha', 'set', 'config')
let def = config.default
if (config[groupId]) {
return { ...def, ...config[groupId] }
return {
...def,
...config[groupId]
}
}
return def
}

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
}