diff --git a/src/config/config.ts b/src/config/config.ts index a954606..8ae1849 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -1,6 +1,8 @@ import YAML from 'yaml' import fs from 'node:fs' import chokidar from 'chokidar' +import { join } from 'node:path' +import { CONFIG_DEFAULT_PATH, CONFIG_INIT_PATH } from './system.js' /** * ******** @@ -13,17 +15,17 @@ class Cfg { /** 监听文件 */ watcher = { config: {}, defSet: {} } - constructor () { + constructor() { this.initCfg() } - + /** * 初始化配置 */ - initCfg () { - let path = 'config/config/' - let pathDef = 'config/default_config/' + initCfg() { + const path = CONFIG_INIT_PATH + const pathDef = CONFIG_DEFAULT_PATH const files = fs.readdirSync(pathDef).filter(file => file.endsWith('.yaml')) for (let file of files) { if (!fs.existsSync(`${path}${file}`)) { @@ -37,45 +39,45 @@ class Cfg { /** * 机器人qq号 */ - get qq () { + get qq() { return Number(this.getConfig('qq').qq) } /** * 密码 */ - get pwd () { + get pwd() { return this.getConfig('qq').pwd } /** * icqq配置 */ - get bot () { + get bot() { let bot = this.getConfig('bot') let defbot = this.getdefSet('bot') - bot = { ...defbot, ...bot } - bot.platform = this.getConfig('qq').platform - /** 设置data目录,防止pm2运行时目录不对 */ - bot.data_dir = process.cwd() + '/data/icqq/' + this.qq || '' - - if (!bot.ffmpeg_path) delete bot.ffmpeg_path - if (!bot.ffprobe_path) delete bot.ffprobe_path - - return bot + const Config = { ...defbot, ...bot } + Config.platform = this.getConfig('qq').platform + /** + * 设置data目录,防止pm2运行时目录不对 + */ + Config.data_dir = join(process.cwd(), `/data/icqq/${this.qq}`) + if (!Config.ffmpeg_path) delete Config.ffmpeg_path + if (!Config.ffprobe_path) delete Config.ffprobe_path + return Config } /** * */ - get other () { + get other() { return this.getConfig('other') } /** * */ - get redis () { + get redis() { return this.getConfig('redis') } @@ -96,25 +98,29 @@ class Cfg { /** * 主人qq */ - get masterQQ () { - let masterQQ = this.getConfig('other').masterQQ || [] - + get masterQQ() { + const masterQQ = this.getConfig('other')?.masterQQ || [] if (Array.isArray(masterQQ)) { - masterQQ.forEach(qq => { qq = String(qq) }) + return masterQQ.forEach(qq => { qq = String(qq) }) } else { - masterQQ = [String(masterQQ)] + return [String(masterQQ)] } - return masterQQ } + _package = null + /** * package.json */ - get package () { + get package() { if (this._package) return this._package - - this._package = JSON.parse(fs.readFileSync('package.json', 'utf8')) - return this._package + try { + const data = fs.readFileSync('package.json', 'utf8') + this._package = JSON.parse(data) + return this._package + } catch { + return {} + } } /** @@ -122,9 +128,9 @@ class Cfg { * @param groupId * @returns */ - getGroup (groupId = '') { - let config = this.getConfig('group') - let defCfg = this.getdefSet('group') + getGroup(groupId = '') { + const config = this.getConfig('group') + const defCfg = this.getdefSet('group') if (config[groupId]) { return { ...defCfg.default, ...config.default, ...config[groupId] } } @@ -135,9 +141,9 @@ class Cfg { * other配置 * @returns */ - getOther () { - let def = this.getdefSet('other') - let config = this.getConfig('other') + getOther() { + const def = this.getdefSet('other') + const config = this.getConfig('other') return { ...def, ...config } } @@ -145,27 +151,27 @@ class Cfg { * notice配置 * @returns */ - getNotice () { - let def = this.getdefSet('notice') - let config = this.getConfig('notice') + getNotice() { + const def = this.getdefSet('notice') + const config = this.getConfig('notice') return { ...def, ...config } } /** - * + * 得到默认配置 * @param name 配置文件名称 * @returns */ - getdefSet (name) { + getdefSet(name: string) { return this.getYaml('default_config', name) } /** - * 用户配置 + * 得到生成式配置 * @param name * @returns */ - getConfig (name) { + getConfig(name: string) { return this.getYaml('config', name) } @@ -174,7 +180,7 @@ class Cfg { * @param type 默认跑配置-defSet,用户配置-config * @param name 名称 */ - getYaml (type, name) { + getYaml(type, name) { let file = `config/${type}/${name}.yaml` let key = `${type}.${name}` if (this.config[key]) return this.config[key] @@ -195,13 +201,11 @@ class Cfg { * @param type * @returns */ - watch (file, name, type = 'default_config') { - let key = `${type}.${name}` - + watch(file:string, name:string, type = 'default_config') { + const key = `${type}.${name}` if (this.watcher[key]) return - const watcher = chokidar.watch(file) - watcher.on('change', path => { + watcher.on('change', () => { delete this.config[key] if (typeof Bot == 'undefined') return logger.mark(`[修改配置文件][${type}][${name}]`) @@ -209,7 +213,6 @@ class Cfg { this[`change_${name}`]() } }) - this.watcher[key] = watcher } @@ -217,21 +220,19 @@ class Cfg { * * @returns */ - change_qq () { + change_qq() { if (process.argv.includes('login') || !this.qq) return logger.info('修改机器人QQ或密码,请手动重启') } /** - * + * 修改日志等级 */ - async change_bot () { - /** 修改日志等级 */ - let log = await import('./log.js') + async change_bot() { + const log = await import('./log.js') log.default() } - } /** @@ -239,4 +240,4 @@ class Cfg { * * *** */ -export default new Cfg() +export default new Cfg() \ No newline at end of file diff --git a/src/config/init.ts b/src/config/init.ts index a445834..649836f 100644 --- a/src/config/init.ts +++ b/src/config/init.ts @@ -1,100 +1,170 @@ +import fs, { promises } from "node:fs" +import yaml from "yaml" +import { BOT_NAME } from "./system.js" import createQQ from "./qq.js" import setLog from "./log.js" import redisInit from "./redis.js" import { checkRun } from "./check.js" -import fs from "node:fs" -import yaml from "yaml" - -/** 设置标题 */ -process.title = "Miao-Yunzai" +import { join } from "node:path" /** * */ -async function UpdateTitle() { - // 添加一些多余的标题内容 - let title = "Miao-Yunzai" - let qq = await fs.promises.readFile("./config/config/qq.yaml", 'utf-8').then(yaml.parse).catch(() => null) +export async function UpdateTitle() { + /** + * 添加一些多余的标题内容 + */ + let title = BOT_NAME + + // + const qq = await promises.readFile("./config/config/qq.yaml", 'utf-8').then(yaml.parse).catch(() => null) + + /** + * + */ if (qq) { title += `@${qq.qq || ""}` switch (qq.platform) { - case 1: + case 1:{ title += " 安卓手机" break - case 2: + } + case 2:{ title += " aPad" break - case 3: + } + case 3:{ title += " 安卓手表" break - case 4: + } + case 4:{ title += " MacOS" break - case 5: + } + case 5:{ title += " iPad" break - case 6: + } + case 6:{ title += " Tim" break - default: + } + default:{ + break + } } } - /** 设置标题 */ + + /** + * 设置标题 + */ process.title = title } -/** 设置时区 */ +/** + * 初始化事件 + */ +export async function checkInit() { + + /** + * 检查node_modules + */ + if (!fs.existsSync(join(process.cwd(), "./node_modules"))) { + console.log("未安装依赖。。。。") + console.log("请先运行命令:pnpm install -P 安装依赖") + process.exit() + } + + /** + * 检查node_modules/icqq + */ + if(!fs.existsSync(join(process.cwd(), "./node_modules/icqq"))){ + console.log("未安装icqq。。。。") + console.log("请先运行命令:pnpm install -P 安装依赖") + process.exit() + } + + /** + * 检查qq.yaml + */ + await createQQ() + + /** + * 日志设置 + */ + setLog() + + /** + * + */ + logger.mark(`${BOT_NAME} 启动中...`) + + /** + * + */ + await redisInit() + + /** + * + */ + await checkRun() + + /** + * 更新标题 + */ + await UpdateTitle() +} + + +/** + * 设置标题 + */ +process.title = BOT_NAME + +/** + * 设置时区 + */ process.env.TZ = "Asia/Shanghai" +/** + * + */ process.on("SIGHUP", () => process.exit()) -/** 捕获未处理的错误 */ +/** + * 捕获未处理的错误 + */ process.on("uncaughtException", error => { if (typeof logger == "undefined") console.log(error) else logger.error(error) }) -/** 捕获未处理的Promise错误 */ -process.on("unhandledRejection", (error, promise) => { + +/** + * 捕获未处理的Promise错误 + */ +process.on("unhandledRejection", (error) => { if (typeof logger == "undefined") console.log(error) else logger.error(error) }) -/** 退出事件 */ -process.on("exit", async code => { - if (typeof redis != "undefined" && typeof test == "undefined") +/** + * 退出事件 + */ +process.on("exit", async () => { + if (typeof redis != "undefined") { await redis.save() - - if (typeof logger == "undefined") - console.log("Miao-Yunzai 已停止运行") - else - logger.mark(logger.magenta("Miao-Yunzai 已停止运行")) + } + if (typeof logger == "undefined") { + console.log(`${BOT_NAME} 已停止运行`) + } + else { + logger.mark(logger.magenta(`${BOT_NAME} 已停止运行`)) + } }) -await checkInit() /** - * 初始化事件 + * 初始化 */ -async function checkInit() { - /** 检查node_modules */ - if (!fs.existsSync("./node_modules") || !fs.existsSync("./node_modules/icqq")) { - console.log("请先运行命令:pnpm install -P 安装依赖") - process.exit() - } - - /** 检查qq.yaml */ - await createQQ() - - /** 日志设置 */ - setLog() - - logger.mark("Miao-Yunzai 启动中...") - - await redisInit() - - await checkRun() - - //** 更新标题 */ - await UpdateTitle() -} \ No newline at end of file +await checkInit() \ No newline at end of file diff --git a/src/config/qq.ts b/src/config/qq.ts index 80381dd..a479f8c 100644 --- a/src/config/qq.ts +++ b/src/config/qq.ts @@ -1,8 +1,9 @@ import fs from 'fs' import inquirer from 'inquirer' +import chalk from 'chalk' +import { BOT_NAME, CONFIG_DEFAULT_PATH, CONFIG_INIT_PATH } from './system.js' import cfg from './config.js' import { sleep } from '../utils/common.js' -import chalk from 'chalk' /** * 创建qq配置文件 `config/bot/qq.yaml` @@ -20,11 +21,10 @@ export default async function createQQ() { return } - /** * */ - console.log(`欢迎使用${chalk.green('Miao-Yunzai v' + cfg.package.version)}\n请按提示输入完成QQ配置`) + console.log(`欢迎使用${chalk.green(`${BOT_NAME} v` + cfg.package.version)}\n请按提示输入完成QQ配置`) /** @@ -99,8 +99,11 @@ export default async function createQQ() { /** * */ - let file = './config/config/' - let fileDef = './config/default_config/' + const file = `./${CONFIG_INIT_PATH}` + + const fileDef = `./${CONFIG_DEFAULT_PATH}` + + let qq = fs.readFileSync(`${fileDef}qq.yaml`, 'utf8') @@ -136,4 +139,4 @@ export default async function createQQ() { * */ await sleep(2000) -} +} \ No newline at end of file diff --git a/src/config/system.ts b/src/config/system.ts new file mode 100644 index 0000000..670c25d --- /dev/null +++ b/src/config/system.ts @@ -0,0 +1,8 @@ +// 机器人名称 +export const BOT_NAME = 'Miao-Yunzai' +// 生成式配置 +export const CONFIG_INIT_PATH = 'config/config/' +// 默认配置 +export const CONFIG_DEFAULT_PATH = 'config/default_config/' +// redis root +export const REDIS_ROOT_KEY = 'Yz:cache:' \ No newline at end of file diff --git a/src/core/common.ts b/src/core/common.ts new file mode 100644 index 0000000..a04a8e7 --- /dev/null +++ b/src/core/common.ts @@ -0,0 +1,126 @@ +/** + * ************ + * 关于bot处理,属于core + * ************ + */ + +/** + * 发送私聊消息,仅给好友发送 + * @param userId qq号 + * @param msg 消息 + * @param uin 指定bot发送,默认为Bot + */ +export async function relpyPrivate(userId, msg, uin = Bot.uin) { + userId = Number(userId) + let friend = Bot.fl.get(userId) + if (friend) { + logger.mark(`发送好友消息[${friend.nickname}](${userId})`) + return await Bot[uin] + .pickUser(userId) + .sendMsg(msg) + .catch(err => { + logger.mark(err) + }) + } +} + +/** + * 制作转发消息 + * @param e 消息事件 + * @param msg 消息数组 + * @param dec 转发描述 + * @param msgsscr 转发信息是否伪装 + */ +export async function makeForwardMsg( + e: any, + msg: any[] | string = [], + dec: string = '', + msgsscr = false +) { + // 不是数组 + if (!Array.isArray(msg)) msg = [msg] + + // + let name = msgsscr ? e.sender.card || e.user_id : Bot.nickname + + // + const Id = msgsscr ? e.user_id : Bot.uin + + // 是群聊 + if (e.isGroup) { + try { + const Info = await e.bot.getGroupMemberInfo(e.group_id, Id) + name = Info.card || Info.nickname + } catch (err) { + console.error(err) + } + } + + let forwardMsg: + | { + user_id: number + nickname: string + message: any + }[] + | { + data: any + } = [] + + /** + * + */ + for (const message of msg) { + if (!message) continue + forwardMsg.push({ + user_id: Id, + nickname: name, + message: message + }) + } + + /** + * 制作转发内容 + */ + try { + /** + * + */ + if (e?.group?.makeForwardMsg) { + // ? + forwardMsg = await e.group.makeForwardMsg(forwardMsg) + } else if (e?.friend?.makeForwardMsg) { + // ? + forwardMsg = await e.friend.makeForwardMsg(forwardMsg) + } else { + // + return msg.join('\n') + } + + /** + * + */ + if (dec) { + /** + * 处理描述 + */ + if (typeof forwardMsg.data === 'object') { + const Detail = forwardMsg.data?.meta?.detail + if (Detail) { + Detail.news = [{ text: dec }] + } + } else { + /** + * + */ + forwardMsg.data = forwardMsg.data + .replace(/\n/g, '') + .replace(/