From 4fc34783b86163ee323ce33d6206b90f5618f7fa Mon Sep 17 00:00:00 2001 From: yoimiya-kokomi <592981798@qq.com> Date: Sun, 27 Mar 2022 19:24:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=86User=E3=80=81MysApi=E7=9A=84=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E6=94=B9=E4=B8=BA=E4=B8=BB=E6=A1=86=E6=9E=B6=E7=9A=84?= =?UTF-8?q?=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/character.js | 34 ++-- components/User.js | 295 --------------------------------- components/models/UserModel.js | 273 ------------------------------ index.js | 6 +- 4 files changed, 27 insertions(+), 581 deletions(-) delete mode 100644 components/User.js delete mode 100644 components/models/UserModel.js diff --git a/apps/character.js b/apps/character.js index 467b54b6..d15610d6 100644 --- a/apps/character.js +++ b/apps/character.js @@ -22,31 +22,42 @@ export async function init(isUpdate = false) { } // 查看当前角色 -export async function character(e, { render, MysApi }) { - let name = e.msg.replace(/#|老婆|老公|[1|2|5][0-9]{8}/g, "").trim(); +export async function character(e, { render, MysApi, User }) { + + if (!e.msg) { + return; + } + + + let name = e.msg.replace(/#?|老婆|老公|[1|2|5][0-9]{8}/g, "").trim(); let char = Character.get(name); if (!char) { return false; } + + + let check = await User.checkAuth(e, "bind", { + action: "查询角色详情" + }); + if (!check) { + return true; + } + let roleId = char.id; getUrl = MysApi.getUrl; getServer = MysApi.getServer; - let uidRes = await getUid(e); - if (!uidRes.uid && uidRes.isSelf) { - e.reply("请先发送#+你游戏的uid"); + let { selfUser, targetUser } = e; + if (!targetUser.uid) { + e.reply("未能找到查询角色"); return true; } - if (!(await limitGet(e))) return true; + let uid = targetUser.uid; - let uid = uidRes.uid; + let res = await MysApi.requestData(e, uid, "character"); - let res = await mysApi(e, uid, "character", { - role_id: uid, - server: getServer(uid), - }); if (res.retcode == "-1") { return true; @@ -97,7 +108,6 @@ export async function character(e, { render, MysApi }) { bg: getCharacterImg(char.name), ...getCharacterData(avatars), ds: char.getData("name,id,title,desc"), - }, "png"); if (base64) { diff --git a/components/User.js b/components/User.js deleted file mode 100644 index 3b82a9ad..00000000 --- a/components/User.js +++ /dev/null @@ -1,295 +0,0 @@ -import UserModel from "./models/UserModel.js"; -import { segment } from "oicq"; -import fetch from "node-fetch"; -import { MysApi } from "./index.js"; -import md5 from "md5"; - -const getUidByToken = async function (token) { - let ltoken = `ltoken=${token["ltoken"]}ltuid=${token["ltuid"]};`; - let cookie_token = `cookie_token=${token["cookie_token"]} account_id=${token["ltuid"]};`; - ltoken += cookie_token; - let uid = 0; - let url = host + "binding/api/getUserGameRolesByCookie?game_biz=hk4e_cn"; - - await MysApi.fetch(url, { - method: "get", - cookie: ltoken, - error: async () => { - throw `cookie错误:${res.message}`; - }, - success: async (data) => { - for (let val of data.list) { - //米游社默认展示的角色 - if (val.is_chosen) { - uid = val.game_uid; - break; - } - } - if (!uid) { - uid = data.list[0].game_uid; - } - - } - }); - return uid; -}; - - - - - -let User = {}; - -/* -* 在文本中检索uid,若未查找到则返回false -* */ -User.matchUid = function (msg) { - let ret = /[1|2|5][0-9]{8}/g.exec(msg); - if (ret) { - return ret[0]; - } - return false; -} - -/* -* 返回需要绑定 cookie -* -* */ -User.replyNeedBind = function (e, replyMsg = "") { - replyMsg = replyMsg || `您尚未绑定米游社cookie,无法进行操作`; - let helpMsg = "获取cookie后发送至当前聊天窗口即可,Cookie获取方式:https://docs.qq.com/doc/DUWNVQVFTU3liTVlO"; - - if (e.isGroup) { - replyMsg = segment.image(`file:///${_path}/resources/help/help.png`); - e.reply([replyMsg, helpMsg]); - } else { - e.reply(replyMsg); - e.reply(helpMsg); - } - - return false; -}; - -/* -* 获取当前用户消息所查询的目标用户 -* -* 策略:优先级依次递减 -* 1. 消息里包含 uid -* 2. 存在 msg.at,且msg.at 用户 是绑定用户 -* 3. 存在 msg.at 且msg.at 名片包含uid -* 4. 当前用户为绑定用户 -* 5. 当前用户名片包含 uid -* 6. 当前用户存在redis-uid 缓存 -* */ -User.getTargetUser = async function (e, selfUser) { - let res; - let reg = /[1|2|5][0-9]{8}/g; - let msg = e.msg; - - let targetId, targetUser; - - /*-- 有指定的查询目标 --*/ - - /* 消息里包含 uid的话优先匹配 */ - if (e.msg) { - targetId = getUid(e.msg); - if (targetId) { - // 根据targetId查找用户 - targetUser = await User.get({ uid: targetId }); - //存在则返回,不存在则将该uid绑定至当前用户 - if (targetUser) { - return targetUser; - } - - let selfUserBindUid = await selfUser.getRegUid(); - // 当前用户未注册,则将uid绑定至当前用户 - if (!selfUser.isBind || selfUser.uid == targetId) { - await selfUser.setRegUid(targetId) - return selfUser; - } else { - // 当前用户为注册用户,返回 Draft - return User.get({ uid: targetId }, true) - } - - - selfUser.setRegUid(targetId); - return selfUser; - - } - } - - // 如果有at的用户,使用被at的用户 - if (!targetId && e.at) { - targetUser = await User.get({ qq: e.at.qq }); - - // 识别at用户的名片结果。如果at用户无uid信息则使用此结果 - targetId = getUid(e.at.card.toString()); - - if (targetUser) { - targetUser - } - } - - if (targetUser) { - let targetUserUid = await targetUser.getRegUid(); - targetUser.setRegUid(targetUserUid || targetId) - if (!targetId && !targetUserUid) { - // return false; - } - } - - targetUser = selfUser; - // 使用当前用户作为targetUser - if (selfUser.isBind) { - // 设置查询用户为当前用户 - targetUser = selfUser; - - // 从当前用户的昵称中匹配uid - targetId = getUid(e.sender.card.toString()); - } else if (false) { - // selfUser.uid = - } - - selfUser.setRegUid(uid); - - - // 存在查询用户,但无 - if (!targetUser && targetId) { - // 根据uid创建的用户包含uid - return User.get({ uid: targetId }, true); - - } else if (targetUser && targetId) { - // 存在目标用户,但不存在查询uid的话,赋值给targetUser - if (!targetUser.uid && targetId) { - targetUser.uid = targetId; - } - } - - // - if (targetUser) { - targetUser.setLastQuery(targetUser.id); - } - - return targetUser; - - - //从redis获取 - res = await redis.get(`genshin:uid:${e.user_id}`); - if (res) { - redis.expire(`genshin:uid:${e.user_id}`, 2592000); - return { isSelf: true, uid: res }; - } - return { isSelf: true, uid: false }; - -}; - - -/* -* 获取当前 MysApi 的最佳查询User -* -* 策略,优先级依次递减 ( sUid 在下方代指被查询的Uid ) -* 1. 如果 sUid 为绑定用户,优先使用绑定用户自身的 cookie( 在不允许跨系统调用时需传递 allowCrossUid = false ) -* 2. 如果 sUid 24小时内被查询过,优先使用曾经查询过该用户的 cookie -* 3. 如果 当前查询用户为绑定用户,优先使用绑定用户自身的 cookie -* 4. 使用系统cookie : 暂未接管bot逻辑,目前需要传入getBotCookie方法 -* -* */ -User.getReqUser = async function (e, allowCrossUser = true, getBotCookie=false) { - - // 当前用户 - let selfUser = User.get(e.user_id); - - // 被查询用户 - let targetUser = await User.getTargetUser(e); - - // 如果 sUid 为绑定用户,优先使用绑定用户自身的 cookie - if (targetUser.isBind && allowCrossUser) { - return targetUser; - } - - // 如果 sUid 24小时内被查询过,优先使用曾经查询过该用户的 cookie - let lastQueryUser = targetUser.getSourceUser(); - if (lastQueryUser) { - return lastQueryUser; - } - - // 如果 当前查询用户为绑定用户,优先使用绑定用户自身的 cookie - if (selfUser.isBind) { - await targetUser.setSourceUser(selfUser); - return selfUser; - } - - // 使用系统 cookie - // 将系统注册的cookie视作机器人,同样包装为 User 用户返回 - let botUser = User.getAvailableBot(e, true); - if (botUser) { - await targetUser.setSourceUser(botUser); - return botUser; - } - return false; -}; - -/* -* 对当前用户的类型进行检查,并对不符合条件的用户进行回复 -* type: all-不检查,bind-绑定用户(设置了有效的NoteCookie),master-管理员 -* replyMsg:不符合条件的消息 -* */ -User.check = async function (e, type = "all", checkParams = {}) { - let self = User.get(e.user_id); - - let { limit = true, action, replyMsg } = checkParams; - - // 校验频度限制 - if (limit) { - if (!(await limitGet(e))) return true; - } - - switch (type) { - case 'bind': - // 需要是绑定用户 - if (!self.isBind) { - if (!replyMsg) { - action = action || "进行操作"; - replyMsg = "您尚未绑定米游社cookie,无法" + action; - } - User.replyNeedBind(e, replyMsg); - return false; - } - break; - - case 'master': - if (!self.isMaster) { - // 如果主动传递了replyMsg则进行回复,否则静默 - if (replyMsg) { - e.reply(replyMsg) - } - return false; - } - - case 'all': - //不检查权限 - return self; - default: - return false; - } - return self; -}; - -/* -* 获取可用的机器人,作为UserModel返回 -* noticeError: 在无可用机器人时是否 e.reply 错误信息 -* */ -// TODO: 待实现 -User.getAvailableBot = async function (e, noticeError = false) { - let id = md5("BOT_" & md5('cookie')); - - User.get(md5); - User.bindCookie(cookie, { - isBot: true - }); - - return false; -}; - -export default User; - diff --git a/components/models/UserModel.js b/components/models/UserModel.js deleted file mode 100644 index d0d68881..00000000 --- a/components/models/UserModel.js +++ /dev/null @@ -1,273 +0,0 @@ -/* -* UserModel Class -* 提供用户实例相关的操作方法 -* -* * TODO:将与具体用户操作相关的方法逐步迁移到UserModel中,外部尽量只调用实例方法 -* 以确保逻辑收敛且维护性更强 -* */ -import BaseModel from "./BaseModel.js" -import lodash from "lodash"; -import md5 from "md5"; -import { MysApi, Data } from "../index.js"; - -const _path = process.cwd(); -const redisPrefix = "cache"; - -const userInstanceReclaimTime = 60; -let userMap = {}; - -// Redis相关操作方法 -const Cache = { - prefix: "genshin", - async get(type, key) { - return await redis.get(`${Cache.prefix}:${type}:${key}`); - }, - async set(type, key, val, exp = 2592000) { - return await redis.set(`${Cache.prefix}:${type}:${key}`, val, { EX: exp }); - }, - async del(type, key) { - return await redis.del(`${Cache.prefix}:${type}:${key}`); - } -}; -const saveCookieFile = function () { - Data.writeJson("./data/NoteCookie/", "NoteCookie", NoteCookie); -}; - -// UserModel class -class UserModel extends BaseModel { - - // 初始化用户 - constructor(id) { - super(); - // 一个id对应一个用户,根据id检索用户信息 - this.id = id; - - // 检索是否存在NoteCookie信息 - let data = NoteCookie[id]; - - if (data) { - this._data = data; - this.isPush = data.isPush; - this.isSignAuto = data.isSignAuto; - this.uid = data.uid; - } else { - this._data = {}; - } - } - - // 是绑定的cookie用户 - // 需要存在NoteCookie记录且存在 cookie 与 uid 才认为是正确记录 - get isBind() { - let dbData = NoteCookie[this.id]; - return !!(dbData && dbData.cookie && dbData.uid); - } - - // 是否是管理员 - // TODO - get isMaster() { - return !this.isBot && BotConfig.masterQQ && BotConfig.masterQQ.includes(Number(this.id)); - } - - get isBot() { - // todo - return false; - } - - // 获取当前用户cookie - get cookie() { - return this._data.cookie; - } - - // 获取当前用户uid - get uid() { - return this._uid || this._data.uid || this._reg_uid; - } - - set uid(uid) { - this._uid = uid; - this._reg_uid = uid; - } - - // 保存用户信息 - /* - async _save() { - // todo - return - - let data = NoteCookie[this.id] || this._data || {}; - - // 将信息更新至 NoteCookie - data.id = this.id; - data.uid = this._uid || this._data.uid; - data.cookie = this._cookie || this._data.cookie; - data.isPush = this.isPush; - data.isAutoSign = !!this.isAutoSign; - - // 保存信息 - NoteCookie[this.id] = data; - this._data = data; - saveCookieFile(); - - // 建立当前用户相关缓存 - await this.refreshCache(); - return this; - } - */ - - // 设置&更新用户缓存 - async refreshCache() { - // 设置缓存 - await Cache.set("id-uid", this.qq, this.uid); - await Cache.set("uid-id", this.uid, this.id); - Bot.logger.mark(`绑定用户:QQ${this.id},UID${this.uid}`); - } - - // 删除用户缓存 - async delCache() { - await Cache.del("id-uid", this.id); - await Cache.del("uid-id", this.uid); - } - - - // 获取曾经查询过当前用户的人 - async getSourceUser() { - let lastQuery = await Cache.get("id-source", this.id); - - if (lastQuery) { - return UserModel.get(lastQuery); - } - - return false; - } - - // 设置曾经查询过当前用户的人,缓存23小时 - async setSourceUser(user) { - await Cache.set("id-source", this.id, user.id, 3600 * 23); - } - - // 删除曾经查询过当前用户的人 - async delSourceUser() { - await Cache.del("id-source", this.id); - } - - - /* 获取当前用户注册的uid - * - * 1. 如果是绑定用户,优先返回当前绑定的uid(cookie 对应uid) - * 2. 返回redis中存储的uid - * - * 注:redis uid需要主动调用一次 getRegUid 才能被this.uid访问到 - * - * */ - async getRegUid() { - if (this.isBind) { - return this.uid; - } - if (!this._reg_uid) { - let uid = await Cache.get('id-regUid', this.id); - if (uid) { - this._reg_uid = uid; - } - } - return this._reg_uid; - } - - async setRegUid(uid) { - // 只有非绑定用户才设置 注册uid - if (!this.isBind) { - this._reg_uid = uid; - Cache.set('id-regUid', this.id, uid); - Cache.set('regUid-id', this.uid, this.id); - } - } -} - - - -/* UserModel static function */ - -/* -* 获取用户实例 -* query为获取条件,默认为 id -* -* */ -UserModel.get = async function (query, getDraftWhenNotFound = false) { - let user = await getUser(query, getDraftWhenNotFound); - - user._reclaimFn && clearTimeout(user._reclaimFn); - user._reclaimFn = setTimeout(() => { - delete userMap[user.id]; - }, userInstanceReclaimTime); - userMap[user.id] = user; - - return user; -}; - -// 格式化查询 -const formatQuery = function (query) { - if (typeof (query) === "string") { - return { id: query }; - } - return query; -}; - -let getUser = async function (query, getDraftWhenNotFound = false) { - query = formatQuery(query); - - let id = ""; - // 根据id获取用户 - if (query.id) { - id = query.id; - } else if (query.uid) { - // 根据uid检索id - id = await Cache.get("uid-id", query.uid); - if (!id) { - // 如未查找到,则从注册uid中检索 - id = await Cache.get("regUid-id", query.uid) - } - } else if (query.token) { - // 根据token检索id - // 不常用,仅用在机器人绑定环节 - id = await Cache.get("token-id", query.token); - } - - // 已有实例优先使用已有的 - if (userMap[id]) { - return userMap[id]; - } - - // 如果是注册用户,则返回新instance - if (NoteCookie[id]) { - return new UserModel(id); - } - - // 如果允许返回Draft,则生成并返回 - if (getDraftWhenNotFound) { - return getDraft(query); - } - - // 未查询到用户则返回false - return false; -} - -let getDraft = function (query) { - let id = ''; - if (query.id) { - id = query.id; - } else if (query.uid) { - id = '_UID_' + query.uid; - } else if (query.token) { - id = "_CK_" + md5(query.token); - } - - let user = new UserModel(id); - user.id = query.id; - user.uid = query.uid; - user.cookie = query.cookie; - return user; -} - - - - -export default UserModel; diff --git a/index.js b/index.js index 76f5eb59..dbfd2cb7 100644 --- a/index.js +++ b/index.js @@ -1,25 +1,29 @@ export const rule = { character: { + prehash: true, reg: "^#(.*)(#.*)?$", priority: 208, describe: "【#刻晴】角色详情", }, setCharacterImg: { + prehash: true, reg: "^#(添加|更新)(.*)图片(#.*)?(上|右|下|左)?$", priority: 208, }, userStat: { + prehash: true, reg: "^#*user\s*\d*", priority: 200 }, rebuildCookie: { + prehash: true, reg: "#rebuild" } }; import { character, setCharacterImg } from "./apps/character.js"; -import {userStat, rebuildCookie} from "./apps/admin.js"; +import { userStat, rebuildCookie } from "./apps/admin.js"; export { character, setCharacterImg, userStat, rebuildCookie };