import plugin from '../../lib/plugins/plugin.js' import { createRequire } from 'module' import lodash from 'lodash' import fs from 'node:fs' import { Restart } from './restart.js' import common from '../../lib/common/common.js' const require = createRequire(import.meta.url) const { exec, execSync } = require('child_process') let uping = false export class update extends plugin { constructor() { super({ name: '更新', dsc: '#更新 #强制更新', event: 'message', priority: 4000, rule: [ { reg: '^#更新日志', fnc: 'updateLog' }, { reg: '^#(强制)?更新', fnc: 'update' }, { reg: '^#(静默)?全部(强制)?更新$', fnc: 'updateAll', permission: 'master' } ] }) this.typeName = 'Miao-Yunzai' this.messages = [] } async update() { if (!this.e.isMaster) return false if (uping) return this.reply('已有命令更新中..请勿重复操作') if (/详细|详情|面板|面版/.test(this.e.msg)) return false /** 获取插件 */ let plugin = this.getPlugin() if (plugin === false) return false /** 执行更新 */ if (plugin === '') { await this.runUpdate('') await common.sleep(1000) plugin = this.getPlugin('miao-plugin') await this.runUpdate(plugin) } else { await this.runUpdate(plugin) } /** 是否需要重启 */ if (this.isUp) { // await this.reply('即将执行重启,以应用更新') setTimeout(() => this.restart(), 2000) } } getPlugin(plugin = '') { if (!plugin) { plugin = this.e.msg.replace(/#(强制)?更新(日志)?/, '') if (!plugin) return '' } if (!fs.existsSync(`plugins/${plugin}/.git`)) return false this.typeName = plugin return plugin } async execSync(cmd) { return new Promise((resolve, reject) => { exec(cmd, { windowsHide: true }, (error, stdout, stderr) => { resolve({ error, stdout, stderr }) }) }) } async runUpdate(plugin = '') { this.isNowUp = false let cm = 'git pull --no-rebase' let type = '更新' if (this.e.msg.includes('强制')) { type = '强制更新' cm = `git reset --hard && git pull --rebase --allow-unrelated-histories` } if (plugin) cm = `cd "plugins/${plugin}" && ${cm}` this.oldCommitId = await this.getcommitId(plugin) logger.mark(`${this.e.logFnc} 开始${type}:${this.typeName}`) await this.reply(`开始${type} ${this.typeName}`) uping = true const ret = await this.execSync(cm) uping = false if (ret.error) { logger.mark(`${this.e.logFnc} 更新失败:${this.typeName}`) this.gitErr(ret.error, ret.stdout) return false } const time = await this.getTime(plugin) if (/Already up|已经是最新/g.test(ret.stdout)) { await this.reply(`${this.typeName} 已是最新\n最后更新时间:${time}`) } else { await this.reply(`${this.typeName} 更新成功\n更新时间:${time}`) this.isUp = true await this.reply(await this.getLog(plugin)) } logger.mark(`${this.e.logFnc} 最后更新时间:${time}`) return true } async getcommitId(plugin = '') { let cm = 'git rev-parse --short HEAD' if (plugin) cm = `cd "plugins/${plugin}" && ${cm}` const commitId = await execSync(cm, { encoding: 'utf-8' }) return lodash.trim(commitId) } async getTime(plugin = '') { let cm = 'git log -1 --pretty=%cd --date=format:"%F %T"' if (plugin) cm = `cd "plugins/${plugin}" && ${cm}` let time = '' try { time = await execSync(cm, { encoding: 'utf-8' }) time = lodash.trim(time) } catch (error) { logger.error(error.toString()) time = '获取时间失败' } return time } async gitErr(err, stdout) { const msg = '更新失败!' const errMsg = err.toString() stdout = stdout.toString() if (errMsg.includes('Timed out')) { const remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '') return this.reply(`${msg}\n连接超时:${remote}`) } if (/Failed to connect|unable to access/g.test(errMsg)) { const remote = errMsg.match(/'(.+?)'/g)[0].replace(/'/g, '') return this.reply(`${msg}\n连接失败:${remote}`) } if (errMsg.includes('be overwritten by merge')) { return this.reply(`${msg}\n存在冲突:\n${errMsg}\n请解决冲突后再更新,或者执行#强制更新,放弃本地修改`) } if (stdout.includes('CONFLICT')) { return this.reply(`${msg}\n存在冲突:\n${errMsg}${stdout}\n请解决冲突后再更新,或者执行#强制更新,放弃本地修改`) } return this.reply([errMsg, stdout]) } async updateAll() { const dirs = fs.readdirSync('./plugins/') const originalReply = this.reply const testReg = /^#静默全部(强制)?更新$/.test(this.e.msg) if (testReg) { await this.reply(`开始执行静默全部更新,请稍等...`) this.reply = (message) => { this.messages.push(message) } } await this.runUpdate() for (let plu of dirs) { plu = this.getPlugin(plu) if (plu === false) continue await common.sleep(1500) await this.runUpdate(plu) } if (testReg) { await this.reply(await common.makeForwardMsg(this.e, this.messages)) } if (this.isUp) { // await this.reply('即将执行重启,以应用更新') setTimeout(() => this.restart(), 2000) } this.reply = originalReply } restart() { new Restart(this.e).restart() } async getLog(plugin = '') { let cm = 'git log -100 --pretty="%h||[%cd] %s" --date=format:"%F %T"' if (plugin) cm = `cd "plugins/${plugin}" && ${cm}` let logAll try { logAll = await execSync(cm, { encoding: 'utf-8' }) } catch (error) { logger.error(error.toString()) await this.reply(error.toString()) } if (!logAll) return false logAll = logAll.trim().split('\n') let log = [] for (let str of logAll) { str = str.split('||') if (str[0] == this.oldCommitId) break if (str[1].includes('Merge branch')) continue log.push(str[1]) } let line = log.length log = log.join('\n\n') if (log.length <= 0) return '' let end = '' try { cm = 'git config -l' if (plugin) cm = `cd "plugins/${plugin}" && ${cm}` end = await execSync(cm, { encoding: 'utf-8' }) end = end.match(/remote\..*\.url=.+/g).join('\n\n').replace(/remote\..*\.url=/g, '').replace(/\/\/([^@]+)@/, '//') } catch (error) { logger.error(error.toString()) await this.reply(error.toString()) } return common.makeForwardMsg(this.e, [log, end], `${plugin || 'Miao-Yunzai'} 更新日志,共${line}条`) } async updateLog() { const plugin = this.getPlugin() if (plugin === false) return false return this.reply(await this.getLog(plugin)) } }