diff --git a/CHANGELOG.md b/CHANGELOG.md index ec717df..c3f9827 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.2.3 +* 新增地图资源查询功能 + * `#月莲在哪里` 指令类似 `某某物品在哪里` + * 感谢 **@小灰灰** 提供的webAPI接口服务 # 1.2.2 * 重写接口文件去除`promise-retry`跟 `superagent`依赖 * 自动签到增加推送签到信息以及成功信息 diff --git a/apps/index.js b/apps/index.js index 4f2db7c..dbe6d80 100644 --- a/apps/index.js +++ b/apps/index.js @@ -7,6 +7,7 @@ import { versionInfo, help } from "./help.js"; +import {genShenMap} from './map.js' import { Note, DailyNoteTask, @@ -38,7 +39,7 @@ export { sign, versionInfo,cloudToken, Note_appoint,signTask, - pokeNote, + pokeNote,genShenMap, cookiesDocHelp, sysCfg, help,updCookie, @@ -47,6 +48,7 @@ export { Note, }; import gsCfg from '../model/gsCfg.js'; +import Data from "../components/Data.js"; const _path = process.cwd(); let rule = { @@ -66,6 +68,10 @@ let rule = { reg: "^#*(体力|树脂|查询体力|便笺|便签)$", describe: "体力", }, + genShenMap:{ + reg: "^#*(.*)(在(哪|那)里)$", + describe: "地图资源查询 #**在哪里", + }, Note_appoint: { reg: "^#体力模板(设置(.*)|列表(.*))$", describe: "体力模板设置", @@ -87,6 +93,7 @@ lodash.forEach(rule, (r) => { task(); //定时任务 async function task() { + if (typeof test != "undefined") return; let set = gsCfg.getfileYaml(`${_path}/plugins/xiaoyao-cvs-plugin/config/`, "config") schedule.scheduleJob(set.mysBbsTime, function() { @@ -105,6 +112,7 @@ async function task() { signTask('cloud') } }); + } diff --git a/apps/map.js b/apps/map.js new file mode 100644 index 0000000..124f585 --- /dev/null +++ b/apps/map.js @@ -0,0 +1,42 @@ +import { + Cfg, + Data +} from "../components/index.js"; +import fs from "fs"; +import fetch from "node-fetch"; +import utils from "../model/mys/utils.js"; +import { + segment +} from "oicq"; +let path = './data/map' + +export async function genShenMap(e){ + let msg= e.msg.replace(/#|(哪|那)|里|在/g,"") + var urlFile = fs.readdirSync(`./data/map/`); + let msgPath=`${path}/${msg}.jpg` + if(urlFile.includes(`${msg}.jpg`)){ + await e.reply(segment.image(`file://${msgPath}`)) + return true; + } + let url=`https://map.minigg.cn/map/get_map?resource_name=${msg}&map_id=2&is_cluster=false` + let res=await fetch(url,{method:'get'}) + try{ + res=await res.json() + if(res.retcode==-1){ + await e.reply(`${msg}:${res.message}`) + return true; + } + }catch(ex){ + await Data.downFile(url,msgPath) + await e.reply(segment.image(`file://${msgPath}`)) + return true; + } + return false; +} +init() +/** 初始化创建配置文件 */ + function init () { + if (!fs.existsSync(path)) { + fs.mkdirSync(path) + } + } \ No newline at end of file diff --git a/apps/user.js b/apps/user.js index 174290a..69bbd3b 100644 --- a/apps/user.js +++ b/apps/user.js @@ -211,7 +211,7 @@ export async function cloudToken(e) { e.yuntoken = yuntoken; let res=await user.cloudSeach() if (res.retcode != 0) { - e.reply(objData.message) + e.reply(res.message) return true; } let datalist = { diff --git a/components/Data.js b/components/Data.js index f4c77a9..ea57eeb 100644 --- a/components/Data.js +++ b/components/Data.js @@ -1,167 +1,198 @@ import lodash from "lodash"; import fs from "fs"; +import { pipeline } from 'stream' +import { promisify } from 'util' +import fetch from 'node-fetch' +import path from 'node:path' const _path = process.cwd() let Data = { - /* - * 根据指定的path依次检查与创建目录 - * */ - createDir(rootPath = "", path = "", includeFile = false) { - let pathList = path.split("/"), - nowPath = rootPath; - pathList.forEach((name, idx) => { - name = name.trim(); - if (!includeFile && idx <= pathList.length - 1) { - nowPath += name + "/"; - if (name) { - if (!fs.existsSync(nowPath)) { - fs.mkdirSync(nowPath); - } - } - } - }) - }, - - /* - * 读取json - * */ - readJSON(root, path) { - if (!/\.json$/.test(path)) { - path = path + ".json"; - } - // 检查并创建目录 - Data.createDir(root, path, true); - if (fs.existsSync(`${root}/${path}`)) { - let jsonRet = fs.readFileSync(`${root}/${path}`, "utf8"); - return JSON.parse(jsonRet); - } - return {} - }, - - /* - * 写JSON - * */ - writeJson(root,path, file, data, space = "\t") { - if (!/\.json$/.test(file)) { - file = file + ".json"; - } + /* + * 根据指定的path依次检查与创建目录 + * */ + createDir(rootPath = "", path = "", includeFile = false) { + let pathList = path.split("/"), + nowPath = rootPath; + pathList.forEach((name, idx) => { + name = name.trim(); + if (!includeFile && idx <= pathList.length - 1) { + nowPath += name + "/"; + if (name) { + if (!fs.existsSync(nowPath)) { + fs.mkdirSync(nowPath); + } + } + } + }) + }, - // 检查并创建目录 - Data.createDir(path, true); - return fs.writeFileSync(`${path}/${file}`, JSON.stringify(data, null, space)); - }, - async importModule (path, file, rootPath = _path) { - if (!/\.js$/.test(file)) { - file = file + '.js' - } - // 检查并创建目录 - Data.createDir(_path, path, true) - if (fs.existsSync(`${_path}/${path}/${file}`)) { - let data = await import(`file://${_path}/${path}/${file}`) - return data || {} - } - return {} - }, - /* - * 返回一个从 target 中选中的属性的对象 - * - * keyList : 获取字段列表,逗号分割字符串 - * key1, key2, toKey1:fromKey1, toKey2:fromObj.key - * - * defaultData: 当某个字段为空时会选取defaultData的对应内容 - * toKeyPrefix:返回数据的字段前缀,默认为空。defaultData中的键值无需包含toKeyPrefix - * - * */ + /* + * 读取json + * */ + readJSON(root, path) { + if (!/\.json$/.test(path)) { + path = path + ".json"; + } + // 检查并创建目录 + Data.createDir(root, path, true); + if (fs.existsSync(`${root}/${path}`)) { + let jsonRet = fs.readFileSync(`${root}/${path}`, "utf8"); + return JSON.parse(jsonRet); + } + return {} + }, + mkdirs (dirname) { + if (fs.existsSync(dirname)) { + return true + } else { + if (Data.mkdirs(path.dirname(dirname))) { + fs.mkdirSync(dirname) + return true + } + } + }, + /** + * 下载保存文件 + * @param fileUrl 下载地址 + * @param savePath 保存路径 + */ + async downFile(fileUrl, savePath, param = {}) { + try { + Data.mkdirs(path.dirname(savePath)) + Bot.logger.mark(`[下载文件] ${fileUrl}`) + const response = await fetch(fileUrl, param) + const streamPipeline = promisify(pipeline) + await streamPipeline(response.body, fs.createWriteStream(savePath)) + return true + } catch (err) { + Bot.logger.error(`下载文件错误:${err}`) + return false + } + }, + /* + * 写JSON + * */ + writeJson(root, path, file, data, space = "\t") { + if (!/\.json$/.test(file)) { + file = file + ".json"; + } - getData(target, keyList = "", cfg = {}) { - target = target || {}; - let defaultData = cfg.defaultData || {}; - let ret = {}; - // 分割逗号 - if (typeof (keyList) === "string") { - keyList = keyList.split(","); - } + // 检查并创建目录 + Data.createDir(path, true); + return fs.writeFileSync(`${path}/${file}`, JSON.stringify(data, null, space)); + }, + async importModule(path, file, rootPath = _path) { + if (!/\.js$/.test(file)) { + file = file + '.js' + } + // 检查并创建目录 + Data.createDir(_path, path, true) + if (fs.existsSync(`${_path}/${path}/${file}`)) { + let data = await import(`file://${_path}/${path}/${file}`) + return data || {} + } + return {} + }, + /* + * 返回一个从 target 中选中的属性的对象 + * + * keyList : 获取字段列表,逗号分割字符串 + * key1, key2, toKey1:fromKey1, toKey2:fromObj.key + * + * defaultData: 当某个字段为空时会选取defaultData的对应内容 + * toKeyPrefix:返回数据的字段前缀,默认为空。defaultData中的键值无需包含toKeyPrefix + * + * */ - lodash.forEach(keyList, (keyCfg) => { - // 处理通过:指定 toKey & fromKey - let _keyCfg = keyCfg.split(":"); - let keyTo = _keyCfg[0].trim(), - keyFrom = (_keyCfg[1] || _keyCfg[0]).trim(), - keyRet = keyTo; - if (cfg.lowerFirstKey) { - keyRet = lodash.lowerFirst(keyRet); - } - if (cfg.keyPrefix) { - keyRet = cfg.keyPrefix + keyRet; - } - // 通过Data.getVal获取数据 - ret[keyRet] = Data.getVal(target, keyFrom, defaultData[keyTo], cfg); - }) - return ret; - }, + getData(target, keyList = "", cfg = {}) { + target = target || {}; + let defaultData = cfg.defaultData || {}; + let ret = {}; + // 分割逗号 + if (typeof(keyList) === "string") { + keyList = keyList.split(","); + } - getVal(target, keyFrom, defaultValue) { - return lodash.get(target, keyFrom, defaultValue); - }, + lodash.forEach(keyList, (keyCfg) => { + // 处理通过:指定 toKey & fromKey + let _keyCfg = keyCfg.split(":"); + let keyTo = _keyCfg[0].trim(), + keyFrom = (_keyCfg[1] || _keyCfg[0]).trim(), + keyRet = keyTo; + if (cfg.lowerFirstKey) { + keyRet = lodash.lowerFirst(keyRet); + } + if (cfg.keyPrefix) { + keyRet = cfg.keyPrefix + keyRet; + } + // 通过Data.getVal获取数据 + ret[keyRet] = Data.getVal(target, keyFrom, defaultData[keyTo], cfg); + }) + return ret; + }, - getUrlPath(url) { - let reg = /^https*:\/\/(.*)\/(\w+\.(png|jpg|jpeg|webp))(\?.*)?$/; - let ret = reg.exec(url); - if (!ret) { - return false; - } - return { - path: ret[1], - filename: ret[2], - type: ret[3], - url - } - }, - pathExists(root, path) { - if (fs.existsSync(root + "/" + path)) { - return true; - } - path = path.replace("\\", "/"); - const dirList = path.split("/"); - let currentDir = root; + getVal(target, keyFrom, defaultValue) { + return lodash.get(target, keyFrom, defaultValue); + }, - for (let dir of dirList) { - currentDir = currentDir + "/" + dir; - if (!fs.existsSync(currentDir)) { - fs.mkdirSync(currentDir); - } - } - return true; - }, - async asyncPool(poolLimit, array, iteratorFn) { - const ret = []; // 存储所有的异步任务 - const executing = []; // 存储正在执行的异步任务 - for (const item of array) { - // 调用iteratorFn函数创建异步任务 - const p = Promise.resolve().then(() => iteratorFn(item, array)); - // 保存新的异步任务 - ret.push(p); + getUrlPath(url) { + let reg = /^https*:\/\/(.*)\/(\w+\.(png|jpg|jpeg|webp))(\?.*)?$/; + let ret = reg.exec(url); + if (!ret) { + return false; + } + return { + path: ret[1], + filename: ret[2], + type: ret[3], + url + } + }, + pathExists(root, path) { + if (fs.existsSync(root + "/" + path)) { + return true; + } + path = path.replace("\\", "/"); + const dirList = path.split("/"); + let currentDir = root; - // 当poolLimit值小于或等于总任务个数时,进行并发控制 - if (poolLimit <= array.length) { - // 当任务完成后,从正在执行的任务数组中移除已完成的任务 - const e = p.then(() => executing.splice(executing.indexOf(e), 1)); - executing.push(e); // 保存正在执行的异步任务 - if (executing.length >= poolLimit) { - // 等待较快的任务执行完成 - await Promise.race(executing); - } - } - } - return Promise.all(ret); - }, + for (let dir of dirList) { + currentDir = currentDir + "/" + dir; + if (!fs.existsSync(currentDir)) { + fs.mkdirSync(currentDir); + } + } + return true; + }, + async asyncPool(poolLimit, array, iteratorFn) { + const ret = []; // 存储所有的异步任务 + const executing = []; // 存储正在执行的异步任务 + for (const item of array) { + // 调用iteratorFn函数创建异步任务 + const p = Promise.resolve().then(() => iteratorFn(item, array)); + // 保存新的异步任务 + ret.push(p); + + // 当poolLimit值小于或等于总任务个数时,进行并发控制 + if (poolLimit <= array.length) { + // 当任务完成后,从正在执行的任务数组中移除已完成的任务 + const e = p.then(() => executing.splice(executing.indexOf(e), 1)); + executing.push(e); // 保存正在执行的异步任务 + if (executing.length >= poolLimit) { + // 等待较快的任务执行完成 + await Promise.race(executing); + } + } + } + return Promise.all(ret); + }, + + sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } - sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); - } - } export default Data; diff --git a/model/mys/utils.js b/model/mys/utils.js index b9e59e0..bdb4b48 100644 --- a/model/mys/utils.js +++ b/model/mys/utils.js @@ -10,7 +10,7 @@ export async function sleepAsync(sleepms) { export async function randomSleepAsync(end) { - let sleep = 3 * 1000 + _.random((end || 5) * 1000); + let sleep = 4 * 1000 + _.random((end || 5) * 1000); await sleepAsync(sleep); } export function randomString(length, os = false) { @@ -35,6 +35,7 @@ export async function redisSet(userId="all", type = 'bbs', data, time=0) { EX: parseInt(new_date) }); } + /** * 发送私聊消息,仅给好友发送 * @param user_id qq号 diff --git a/model/user.js b/model/user.js index 9b6c826..e87db32 100644 --- a/model/user.js +++ b/model/user.js @@ -94,7 +94,7 @@ export default class user { let res try { message += `**${forum.name}**\n` - res = await this.getData("userGameInfo", forum) + res = await this.getData("userGameInfo", forum ) await utils.sleepAsync(3000) //等几毫秒免得请求太频繁了 if (res?.data?.list?.length === 0 || !res?.data?.list) { message += `签到: 未绑定${forum.name}信息\n`; @@ -109,7 +109,7 @@ export default class user { let data = Object.assign({}, forum, item) item.is_sign = true; item.upName = forum.name - res = await this.getData("isSign", data) + res = await this.getData("isSign", data,false) await utils.sleepAsync(500) item.total_sign_day = res?.data?.total_sign_day if (res?.data?.is_sign) { @@ -123,7 +123,7 @@ export default class user { if (signTime) { //有数据的时候不得行必须出去 if (!mysTask) { - message += "${item.nickname}-${item.game_uid}:验证码失败请等待6分钟后重试或者手动上米游社签到~"; + message += `${item.nickname}-${item.game_uid}:验证码失败请等待6分钟后重试或者手动上米游社签到~`; break; } else { await utils.sleepAsync(60000 * 6) //等6分钟再说 @@ -131,7 +131,7 @@ export default class user { } } await utils.sleepAsync(2000) - res = await this.getData("sign", data) + res = await this.getData("sign", data,false) if (res?.data?.gt) { //进行3次验证码访问签到加高通过概率 let validate = await this.geetest(res.data) if (validate) { @@ -140,7 +140,7 @@ export default class user { header["x-rpc-validate"] = validate header["x-rpc-seccode"] = `${validate}|jordan` data.headers = header - res = await this.getData("sign", data) + res = await this.getData("sign", data,false) if (!res?.data?.gt) { if(this.allSign){ this.allSign[forum.name].sign++; @@ -166,10 +166,9 @@ export default class user { break; } } - await utils.sleepAsync(2000) } //获取签到信息和奖励信息 - const SignInfo = await this.getData("home", data) + const SignInfo = await this.getData("home", data,false) if (SignInfo) { let awards = SignInfo.data.awards[item.total_sign_day - 1]; item.awards = awards?.name + "*" + awards?.cnt @@ -256,7 +255,7 @@ export default class user { let Share = 0; let sumcount = 0; message += `\n**${forum.name}**\n` - res = await this.getData("bbsSign", forum) + res = await this.getData("bbsSign", forum,false) if (res?.retcode == 1034) { message += `社区签到: 验证码失败\n`; challenge = await this.bbsGeetest() @@ -264,14 +263,14 @@ export default class user { forum["headers"] = { "x-rpc-challenge": challenge } - await this.getData("bbsSign", forum) + await this.getData("bbsSign", forum,false) } } else { message += `社区签到: ${res.message}\n`; } Bot.logger.mark(`${this.e.user_id}:${this.e.uid}:${forum.name} 社区签到结果: [${res.message}]`); await utils.randomSleepAsync(); - res = await this.getData("bbsPostList", forum) + res = await this.getData("bbsPostList", forum,false) sumcount++; let postList = res.data.list; let postId @@ -293,13 +292,13 @@ export default class user { "x-rpc-challenge": challenge, } } - await this.getData("bbsPostFull", data) + await this.getData("bbsPostFull", data,false) } } await utils.randomSleepAsync(10); res = await this.getData("bbsVotePost", { postId - }) + },false) if (res?.message && res?.retcode == 0) { Vote++; } @@ -312,7 +311,7 @@ export default class user { "x-rpc-challenge": challenge, } } - await this.getData("bbsVotePost", data) + await this.getData("bbsVotePost", data,false) } } await utils.randomSleepAsync(2); @@ -320,7 +319,7 @@ export default class user { let sharePost = postList[0].post; res = await this.getData("bbsShareConf", { postId - }) + },false) if (res?.message && res?.retcode == 0) { Share++; } @@ -358,7 +357,7 @@ export default class user { mysTask = true; let tips = ['开始米社签到任务'] - let time = userIdList.length * 25 + 5 + let time = userIdList.length * 25 + 5+ (userIdList.length/3 * 60) let finishTime = moment().add(time, 's').format('MM-DD HH:mm:ss') tips.push(`\n签到用户:${userIdList.length}个`) tips.push(`\n预计需要:${this.countTime(time)}`) @@ -580,7 +579,8 @@ export default class user { } }; this.e = e; - await this.getbbsSign(this.ForumData); + let res= await this.getbbsSign(this.ForumData); + e.reply(res.message) await utils.sleepAsync(10000); } }