update: 修改
This commit is contained in:
commit
bc9cf29004
|
@ -0,0 +1,12 @@
|
||||||
|
# System-Plugin
|
||||||
|
|
||||||
|
Miao-Yunzai V4 插件开发示例
|
||||||
|
|
||||||
|
## 使用教程
|
||||||
|
|
||||||
|
- 安装源码
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone --depth=1 -b dev https://github.com/yoimiya-kokomi/Miao-Yunzai.git
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
/**
|
||||||
|
* ***********
|
||||||
|
* 不想开启的功能,自行注释
|
||||||
|
* *****
|
||||||
|
*/
|
||||||
|
export * from './apps/add'
|
||||||
|
export * from './apps/disFriPoke'
|
||||||
|
export * from './apps/disablePrivate'
|
||||||
|
export * from './apps/friend'
|
||||||
|
export * from './apps/invite'
|
||||||
|
export * from './apps/quit'
|
||||||
|
export * from './apps/restart'
|
||||||
|
export * from './apps/sendLog'
|
||||||
|
export * from './apps/status'
|
||||||
|
export * from './apps/update'
|
||||||
|
export * from './apps/example2'
|
||||||
|
export * from './apps/newcomer'
|
||||||
|
export * from './apps/outNotice'
|
|
@ -0,0 +1,966 @@
|
||||||
|
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import lodash from 'lodash'
|
||||||
|
import { pipeline } from 'stream'
|
||||||
|
import { promisify } from 'util'
|
||||||
|
import fetch from 'node-fetch'
|
||||||
|
import moment from 'moment'
|
||||||
|
import { ConfigController as cfg } from '#miao/config'
|
||||||
|
import { plugin } from '#miao/core'
|
||||||
|
import * as common from '#miao/core'
|
||||||
|
|
||||||
|
const textArr = {}
|
||||||
|
|
||||||
|
export class add extends plugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
path = './data/textJson/'
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
facePath = './data/face/'
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
isGlobal = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
/**
|
||||||
|
name: '添加表情',
|
||||||
|
dsc: '添加表情,文字等',
|
||||||
|
*/
|
||||||
|
super({
|
||||||
|
event: 'message',
|
||||||
|
priority: 50000,
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rule
|
||||||
|
*/
|
||||||
|
this.rule = [
|
||||||
|
{
|
||||||
|
reg: '^#(全局)?添加(.*)',
|
||||||
|
fnc: this.add.name
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: '^#(全局)?删除(.*)',
|
||||||
|
fnc: this.del.name
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: '(.*)',
|
||||||
|
fnc: this.getText.name,
|
||||||
|
log: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: '^#+(全局)?(?:查看|查询)(?:表情|词条)(.+)$',
|
||||||
|
fnc: this.faceDetail.name
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: '#(全局)?(表情|词条)(.*)',
|
||||||
|
fnc: this.list.name
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async init() {
|
||||||
|
if (!fs.existsSync(this.path)) {
|
||||||
|
fs.mkdirSync(this.path)
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(this.facePath)) {
|
||||||
|
fs.mkdirSync(this.facePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async accept() {
|
||||||
|
/** 处理消息 */
|
||||||
|
if (this.e.atBot && this.e.msg && this.e?.msg.includes('添加') && !this.e?.msg.includes('#')) {
|
||||||
|
this.e.msg = '#' + this.e.msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
get grpKey() {
|
||||||
|
return `Yz:group_id:${this.e.user_id}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async add() {
|
||||||
|
this.isGlobal = this.e?.msg.includes("全局");
|
||||||
|
await this.getGroupId()
|
||||||
|
|
||||||
|
if (!this.group_id) {
|
||||||
|
this.e.reply('请先在群内触发表情,确定添加的群')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initTextArr()
|
||||||
|
|
||||||
|
if (!this.checkAuth()) return
|
||||||
|
if (!this.checkKeyWord()) return
|
||||||
|
if (await this.singleAdd()) return
|
||||||
|
/** 获取关键词 */
|
||||||
|
this.getKeyWord()
|
||||||
|
|
||||||
|
if (!this.keyWord) {
|
||||||
|
this.e.reply('添加错误:没有关键词')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (/uid/i.test(this.keyWord)) {
|
||||||
|
this.e.reply('请勿添加特殊关键词')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setContext('addContext')
|
||||||
|
|
||||||
|
await this.e.reply('请发送添加内容', false, { at: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async getGroupId() {
|
||||||
|
/** 添加全局表情,存入到机器人qq文件中 */
|
||||||
|
if (this.isGlobal) {
|
||||||
|
this.group_id = this.e.bot.uin;
|
||||||
|
return this.e.bot.uin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.e.isGroup) {
|
||||||
|
this.group_id = this.e.group_id
|
||||||
|
redis.setEx(this.grpKey, 3600 * 24 * 30, String(this.group_id))
|
||||||
|
return this.group_id
|
||||||
|
}
|
||||||
|
|
||||||
|
// redis获取
|
||||||
|
let groupId = await redis.get(this.grpKey)
|
||||||
|
if (groupId) {
|
||||||
|
this.group_id = groupId
|
||||||
|
return this.group_id
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
checkAuth() {
|
||||||
|
if (this.e.isMaster) return true
|
||||||
|
|
||||||
|
let groupCfg = cfg.getGroup(this.group_id)
|
||||||
|
if (groupCfg.imgAddLimit == 2) {
|
||||||
|
this.e.reply('暂无权限,只有主人才能操作')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (groupCfg.imgAddLimit == 1) {
|
||||||
|
if (!this.e.bot.gml.has(this.group_id)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!this.e.bot.gml.get(this.group_id).get(this.e.user_id)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!this.e.member.is_admin) {
|
||||||
|
this.e.reply('暂无权限,只有管理员才能操作')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.e.isGroup && groupCfg.addPrivate != 1) {
|
||||||
|
this.e.reply('禁止私聊添加')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
checkKeyWord() {
|
||||||
|
if (this.e.img && this.e.img.length > 1) {
|
||||||
|
this.e.reply('添加错误:只能发送一个表情当关键词')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.e.at) {
|
||||||
|
let at = lodash.filter(this.e.message, (o) => { return o.type == 'at' && o.qq != this.e.bot.uin })
|
||||||
|
if (at.length > 1) {
|
||||||
|
this.e.reply('添加错误:只能@一个人当关键词')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.e.img && this.e.at) {
|
||||||
|
this.e.reply('添加错误:没有关键词')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单独添加
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async singleAdd() {
|
||||||
|
if (this.e.message.length != 2) return false
|
||||||
|
let msg = lodash.keyBy(this.e.message, 'type')
|
||||||
|
if (!this.e.msg || !msg.image) return false
|
||||||
|
|
||||||
|
// #全局添加文字+表情包,无法正确添加到全局路径
|
||||||
|
this.e.isGlobal = this.isGlobal;
|
||||||
|
let keyWord = this.e.msg.replace(/#|#|图片|表情|添加|全局/g, '').trim()
|
||||||
|
if (!keyWord) return false
|
||||||
|
|
||||||
|
this.keyWord = this.trimAlias(keyWord)
|
||||||
|
this.e.keyWord = this.keyWord
|
||||||
|
|
||||||
|
if (this.e.msg.includes('添加图片')) {
|
||||||
|
this.e.addImg = true
|
||||||
|
}
|
||||||
|
this.e.message = [msg.image]
|
||||||
|
await this.addContext()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取添加关键词
|
||||||
|
*/
|
||||||
|
getKeyWord() {
|
||||||
|
this.e.isGlobal = this.e.msg.includes("全局");
|
||||||
|
|
||||||
|
this.keyWord = this.e.toString()
|
||||||
|
.trim()
|
||||||
|
/** 过滤#添加 */
|
||||||
|
.replace(/#|#|图片|表情|添加|删除|全局/g, '')
|
||||||
|
/** 过滤@ */
|
||||||
|
.replace(new RegExp('{at:' + this.e.bot.uin + '}', 'g'), '')
|
||||||
|
.trim()
|
||||||
|
|
||||||
|
this.keyWord = this.trimAlias(this.keyWord)
|
||||||
|
this.e.keyWord = this.keyWord
|
||||||
|
|
||||||
|
if (this.e.msg.includes('添加图片')) {
|
||||||
|
this.e.addImg = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤别名
|
||||||
|
* @param msg
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
trimAlias(msg) {
|
||||||
|
let groupCfg = cfg.getGroup(this.group_id)
|
||||||
|
let alias = groupCfg.botAlias
|
||||||
|
if (!Array.isArray(alias)) {
|
||||||
|
alias = [alias]
|
||||||
|
}
|
||||||
|
for (let name of alias) {
|
||||||
|
if (msg.startsWith(name)) {
|
||||||
|
msg = lodash.trimStart(msg, name).trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加内容
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async addContext() {
|
||||||
|
this.isGlobal = this.e.isGlobal || this.getContext()?.addContext?.isGlobal;
|
||||||
|
await this.getGroupId()
|
||||||
|
/** 关键词 */
|
||||||
|
let keyWord = this.keyWord || this.getContext()?.addContext?.keyWord
|
||||||
|
let addImg = this.e.addImg || this.getContext()?.addContext?.addImg
|
||||||
|
|
||||||
|
/** 添加内容 */
|
||||||
|
let message = this.e.message
|
||||||
|
|
||||||
|
let retMsg = this.getRetMsg()
|
||||||
|
this.finish('addContext')
|
||||||
|
|
||||||
|
for (let i in message) {
|
||||||
|
if (message[i].type == "at") {
|
||||||
|
if (message[i].qq == this.e.bot.uin) {
|
||||||
|
this.e.reply("添加内容不能@机器人!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (message[i].type == "file") {
|
||||||
|
this.e.reply("添加错误:禁止添加文件");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存用户信息用于追溯添加者
|
||||||
|
message[i].from_user = {
|
||||||
|
card: this.e.sender.card,
|
||||||
|
nickname: this.e.sender.nickname,
|
||||||
|
user_id: this.e.sender.user_id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.length == 1 && message[0].type == 'image') {
|
||||||
|
let local = await this.saveImg(message[0].url, keyWord)
|
||||||
|
if (!local) return
|
||||||
|
message[0].local = local
|
||||||
|
message[0].asface = true
|
||||||
|
if (addImg) message[0].asface = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!textArr[this.group_id]) textArr[this.group_id] = new Map()
|
||||||
|
|
||||||
|
/** 支持单个关键词添加多个 */
|
||||||
|
let text = textArr[this.group_id].get(keyWord)
|
||||||
|
if (text) {
|
||||||
|
text.push(message)
|
||||||
|
textArr[this.group_id].set(keyWord, text)
|
||||||
|
} else {
|
||||||
|
text = [message]
|
||||||
|
textArr[this.group_id].set(keyWord, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.length > 1 && retMsg[0].type != 'image') {
|
||||||
|
retMsg.push(String(text.length))
|
||||||
|
}
|
||||||
|
|
||||||
|
retMsg.unshift('添加成功:')
|
||||||
|
|
||||||
|
this.saveJson()
|
||||||
|
this.e.reply(retMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加成功回复消息
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getRetMsg() {
|
||||||
|
let retMsg = this.getContext()
|
||||||
|
let msg = ''
|
||||||
|
if (retMsg?.addContext?.message) {
|
||||||
|
msg = retMsg.addContext.message
|
||||||
|
|
||||||
|
for (let i in msg) {
|
||||||
|
if (msg[i].type == 'text' && msg[i].text.includes('添加')) {
|
||||||
|
msg[i].text = this.trimAlias(msg[i].text)
|
||||||
|
msg[i].text = msg[i].text.trim().replace(/#|#|图片|表情|添加|全局/g, '')
|
||||||
|
if (!msg[i].text) delete msg[i]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (msg[i].type == 'at') {
|
||||||
|
if (msg[i].qq == this.e.bot.uin) {
|
||||||
|
delete msg[i]
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
msg[i].text = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!msg && this.keyWord) {
|
||||||
|
msg = [this.keyWord]
|
||||||
|
}
|
||||||
|
return lodash.compact(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
saveJson() {
|
||||||
|
let obj = {}
|
||||||
|
for (let [k, v] of textArr[this.group_id]) {
|
||||||
|
obj[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(`${this.path}${this.group_id}.json`, JSON.stringify(obj, '', '\t'))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
saveGlobalJson() {
|
||||||
|
let obj = {};
|
||||||
|
for (let [k, v] of textArr[this.e.bot.uin]) {
|
||||||
|
obj[k] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
`${this.path}${this.e.bot.uin}.json`,
|
||||||
|
JSON.stringify(obj, "", "\t")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @param keyWord
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async saveImg(url, keyWord) {
|
||||||
|
let groupCfg = cfg.getGroup(this.group_id)
|
||||||
|
let savePath = `${this.facePath}${this.group_id}/`
|
||||||
|
|
||||||
|
if (!fs.existsSync(savePath)) {
|
||||||
|
fs.mkdirSync(savePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url)
|
||||||
|
|
||||||
|
keyWord = keyWord.replace(/\.|\\|\/|:|\*|\?|<|>|\|"/g, '_')
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
this.e.reply('添加图片下载失败。。')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let imgSize = (response.headers.get('size') / 1024 / 1024).toFixed(2)
|
||||||
|
if (imgSize > 1024 * 1024 * groupCfg.imgMaxSize) {
|
||||||
|
this.e.reply(`添加失败:表情太大了,${imgSize}m`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let type = response.headers.get('content-type').split('/')[1]
|
||||||
|
if (type == 'jpeg') type = 'jpg'
|
||||||
|
|
||||||
|
if (fs.existsSync(`${savePath}${keyWord}.${type}`)) {
|
||||||
|
keyWord = `${keyWord}_${moment().format('X')}`
|
||||||
|
}
|
||||||
|
|
||||||
|
savePath = `${savePath}${keyWord}.${type}`
|
||||||
|
|
||||||
|
const streamPipeline = promisify(pipeline)
|
||||||
|
await streamPipeline(response.body, fs.createWriteStream(savePath))
|
||||||
|
|
||||||
|
return savePath
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async getText() {
|
||||||
|
if (!this.e.message) return false
|
||||||
|
|
||||||
|
this.isGlobal = false
|
||||||
|
|
||||||
|
await this.getGroupId()
|
||||||
|
|
||||||
|
if (!this.group_id) return false
|
||||||
|
|
||||||
|
this.initTextArr()
|
||||||
|
|
||||||
|
this.initGlobalTextArr()
|
||||||
|
|
||||||
|
let keyWord = this.e.toString()
|
||||||
|
.replace(/#|#/g, '')
|
||||||
|
.replace(`{at:${this.e.bot.uin}}`, '')
|
||||||
|
.trim()
|
||||||
|
|
||||||
|
keyWord = this.trimAlias(keyWord)
|
||||||
|
|
||||||
|
let num = 0
|
||||||
|
if (isNaN(keyWord)) {
|
||||||
|
num = keyWord.trim().match(/[0-9]+$/)?.[0]
|
||||||
|
|
||||||
|
if (!isNaN(num) && !textArr[this.group_id].has(keyWord) && !textArr[this.e.bot.uin].has(keyWord)) {
|
||||||
|
keyWord = lodash.trimEnd(keyWord, num).trim()
|
||||||
|
num--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = textArr[this.group_id].get(keyWord) || []
|
||||||
|
let globalMsg = textArr[this.e.bot.uin].get(keyWord) || []
|
||||||
|
if (lodash.isEmpty(msg) && lodash.isEmpty(globalMsg)) return false
|
||||||
|
|
||||||
|
msg = [...msg, ...globalMsg]
|
||||||
|
/** 如果只有一个则不随机 */
|
||||||
|
if (num >= 0 && msg.length === 1) {
|
||||||
|
msg = msg[num]
|
||||||
|
} else {
|
||||||
|
/** 随机获取一个 */
|
||||||
|
num = lodash.random(0, msg.length - 1)
|
||||||
|
msg = msg[num]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg[0] && msg[0].local) {
|
||||||
|
if (fs.existsSync(msg[0].local)) {
|
||||||
|
let tmp = segment.image(msg[0].local)
|
||||||
|
tmp.asface = msg[0].asface
|
||||||
|
msg = tmp
|
||||||
|
} else {
|
||||||
|
// this.e.reply(`表情已删除:${keyWord}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(msg)) {
|
||||||
|
msg.forEach(m => {
|
||||||
|
/** 去除回复@@ */
|
||||||
|
if (m?.type == 'at') { delete m.text }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.mark(`[发送表情]${this.e.logText} ${keyWord}`)
|
||||||
|
let ret = await this.e.reply(msg)
|
||||||
|
if (!ret) {
|
||||||
|
this.expiredMsg(keyWord, num)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param keyWord
|
||||||
|
* @param num
|
||||||
|
*/
|
||||||
|
expiredMsg(keyWord, num) {
|
||||||
|
logger.mark(`[发送表情]${this.e.logText} ${keyWord} 表情已过期失效`)
|
||||||
|
|
||||||
|
let arr = textArr[this.group_id].get(keyWord)
|
||||||
|
arr.splice(num, 1)
|
||||||
|
|
||||||
|
if (arr.length <= 0) {
|
||||||
|
textArr[this.group_id].delete(keyWord)
|
||||||
|
} else {
|
||||||
|
textArr[this.group_id].set(keyWord, arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveJson()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化已添加内容
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
initTextArr() {
|
||||||
|
if (textArr[this.group_id]) return
|
||||||
|
|
||||||
|
textArr[this.group_id] = new Map()
|
||||||
|
|
||||||
|
let path = `${this.path}${this.group_id}.json`
|
||||||
|
if (!fs.existsSync(path)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let text = JSON.parse(fs.readFileSync(path, 'utf8'))
|
||||||
|
for (let i in text) {
|
||||||
|
if (text[i][0] && !Array.isArray(text[i][0])) {
|
||||||
|
text[i] = [text[i]]
|
||||||
|
}
|
||||||
|
|
||||||
|
textArr[this.group_id].set(String(i), text[i])
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`json格式错误:${path}`)
|
||||||
|
delete textArr[this.group_id]
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加载表情 */
|
||||||
|
let facePath = `${this.facePath}${this.group_id}`
|
||||||
|
|
||||||
|
if (fs.existsSync(facePath)) {
|
||||||
|
const files = fs.readdirSync(`${this.facePath}${this.group_id}`).filter(file => /\.(jpeg|jpg|png|gif)$/g.test(file))
|
||||||
|
for (let val of files) {
|
||||||
|
let tmp = val.split('.')
|
||||||
|
tmp[0] = tmp[0].replace(/_[0-9]{10}$/, '')
|
||||||
|
if (/at|image/g.test(val)) continue
|
||||||
|
|
||||||
|
if (textArr[this.group_id].has(tmp[0])) continue
|
||||||
|
|
||||||
|
textArr[this.group_id].set(tmp[0], [[{
|
||||||
|
local: `${facePath}/${val}`,
|
||||||
|
asface: true
|
||||||
|
}]])
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveJson()
|
||||||
|
} else {
|
||||||
|
fs.mkdirSync(facePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化全局已添加内容
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
initGlobalTextArr() {
|
||||||
|
if (textArr[this.e.bot.uin]) return;
|
||||||
|
|
||||||
|
textArr[this.e.bot.uin] = new Map();
|
||||||
|
|
||||||
|
let globalPath = `${this.path}${this.e.bot.uin}.json`;
|
||||||
|
if (!fs.existsSync(globalPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let text = JSON.parse(fs.readFileSync(globalPath, "utf8"));
|
||||||
|
|
||||||
|
for (let i in text) {
|
||||||
|
if (text[i][0] && !Array.isArray(text[i][0])) {
|
||||||
|
text[i] = [text[i]];
|
||||||
|
}
|
||||||
|
textArr[this.e.bot.uin].set(String(i), text[i]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`json格式错误:${globalPath}`);
|
||||||
|
delete textArr[this.e.bot.uin];
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加载表情 */
|
||||||
|
let globalFacePath = `${this.facePath}${this.e.bot.uin}`;
|
||||||
|
|
||||||
|
if (fs.existsSync(globalFacePath)) {
|
||||||
|
const files = fs
|
||||||
|
.readdirSync(`${this.facePath}${this.e.bot.uin}`)
|
||||||
|
.filter((file) => /\.(jpeg|jpg|png|gif)$/g.test(file));
|
||||||
|
|
||||||
|
for (let val of files) {
|
||||||
|
let tmp = val.split(".");
|
||||||
|
tmp[0] = tmp[0].replace(/_[0-9]{10}$/, "");
|
||||||
|
if (/at|image/g.test(val)) continue;
|
||||||
|
|
||||||
|
if (textArr[this.e.bot.uin].has(tmp[0])) continue;
|
||||||
|
|
||||||
|
textArr[this.e.bot.uin].set(tmp[0], [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
local: `${globalFacePath}/${val}`,
|
||||||
|
asface: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveGlobalJson();
|
||||||
|
} else {
|
||||||
|
fs.mkdirSync(globalFacePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async del() {
|
||||||
|
this.isGlobal = this.e?.msg.includes("全局");
|
||||||
|
await this.getGroupId()
|
||||||
|
if (!this.group_id) return false
|
||||||
|
if (!this.checkAuth()) return
|
||||||
|
|
||||||
|
this.initTextArr()
|
||||||
|
|
||||||
|
let keyWord = this.e.toString().replace(/#|#|图片|表情|删除|全部|全局/g, '')
|
||||||
|
|
||||||
|
keyWord = this.trimAlias(keyWord)
|
||||||
|
|
||||||
|
let num = false
|
||||||
|
let index = 0
|
||||||
|
if (isNaN(keyWord)) {
|
||||||
|
num = keyWord.charAt(keyWord.length - 1)
|
||||||
|
|
||||||
|
if (!isNaN(num) && !textArr[this.group_id].has(keyWord)) {
|
||||||
|
keyWord = lodash.trimEnd(keyWord, num).trim()
|
||||||
|
index = num - 1
|
||||||
|
} else {
|
||||||
|
num = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let arr = textArr[this.group_id].get(keyWord)
|
||||||
|
if (!arr) {
|
||||||
|
// await this.e.reply(`暂无此表情:${keyWord}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
let tmp = []
|
||||||
|
if (num) {
|
||||||
|
if (!arr[index]) {
|
||||||
|
// await this.e.reply(`暂无此表情:${keyWord}${num}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = arr[index]
|
||||||
|
arr.splice(index, 1)
|
||||||
|
|
||||||
|
if (arr.length <= 0) {
|
||||||
|
textArr[this.group_id].delete(keyWord)
|
||||||
|
} else {
|
||||||
|
textArr[this.group_id].set(keyWord, arr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.e.msg.includes('删除全部')) {
|
||||||
|
tmp = arr
|
||||||
|
arr = []
|
||||||
|
} else {
|
||||||
|
tmp = arr.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arr.length <= 0) {
|
||||||
|
textArr[this.group_id].delete(keyWord)
|
||||||
|
} else {
|
||||||
|
textArr[this.group_id].set(keyWord, arr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!num) num = ''
|
||||||
|
|
||||||
|
let retMsg = [{ type: 'text', text: '删除成功:' }]
|
||||||
|
for (let msg of this.e.message) {
|
||||||
|
if (msg.type == 'text') {
|
||||||
|
msg.text = msg.text.replace(/#|#|图片|表情|删除|全部|全局/g, '')
|
||||||
|
|
||||||
|
if (!msg.text) continue
|
||||||
|
}
|
||||||
|
retMsg.push(msg)
|
||||||
|
}
|
||||||
|
if (num > 0) {
|
||||||
|
retMsg.push({ type: 'text', text: num })
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.e.reply(retMsg)
|
||||||
|
|
||||||
|
/** 删除图片 */
|
||||||
|
tmp.forEach(item => {
|
||||||
|
let img = item
|
||||||
|
if (Array.isArray(item)) {
|
||||||
|
img = item[0]
|
||||||
|
}
|
||||||
|
if (img.local) {
|
||||||
|
fs.unlink(img.local, () => { })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.saveJson()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async list() {
|
||||||
|
this.isGlobal = this.e?.msg.includes("全局");
|
||||||
|
|
||||||
|
let page = 1
|
||||||
|
let pageSize = 100
|
||||||
|
let type = 'list'
|
||||||
|
|
||||||
|
await this.getGroupId()
|
||||||
|
if (!this.group_id) return false
|
||||||
|
|
||||||
|
this.initTextArr()
|
||||||
|
|
||||||
|
let search = this.e.msg.replace(/#|#|表情|词条|全局/g, '')
|
||||||
|
|
||||||
|
if (search.includes('列表')) {
|
||||||
|
page = search.replace(/列表/g, '') || 1
|
||||||
|
} else {
|
||||||
|
type = 'search'
|
||||||
|
}
|
||||||
|
|
||||||
|
let list = textArr[this.group_id]
|
||||||
|
|
||||||
|
if (lodash.isEmpty(list)) {
|
||||||
|
await this.e.reply('暂无表情')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let arr = []
|
||||||
|
for (let [k, v] of textArr[this.group_id]) {
|
||||||
|
if (type == 'list') {
|
||||||
|
arr.push({ key: k, val: v, num: arr.length + 1 })
|
||||||
|
} else if (k.includes(search)) {
|
||||||
|
/** 搜索表情 */
|
||||||
|
arr.push({ key: k, val: v, num: arr.length + 1 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = arr.length
|
||||||
|
arr = arr.reverse()
|
||||||
|
|
||||||
|
if (type == 'list') {
|
||||||
|
arr = this.pagination(page, pageSize, arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lodash.isEmpty(arr)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = [], result = [], num = 0
|
||||||
|
for (let i in arr) {
|
||||||
|
if (num >= page * pageSize) break
|
||||||
|
|
||||||
|
let keyWord = await this.keyWordTran(arr[i].key)
|
||||||
|
if (!keyWord) continue
|
||||||
|
if (Array.isArray(keyWord)) {
|
||||||
|
keyWord.unshift(`${num + 1}、`)
|
||||||
|
// keyWord.push('\n')
|
||||||
|
keyWord.push(v => msg.push(v))
|
||||||
|
} else if (keyWord.type) {
|
||||||
|
msg.push(`\n${num + 1}、`, keyWord)
|
||||||
|
} else {
|
||||||
|
msg.push(`${num + 1}、`, keyWord)
|
||||||
|
}
|
||||||
|
num++
|
||||||
|
}
|
||||||
|
/** 数组分段 */
|
||||||
|
for (const i in msg) {
|
||||||
|
result.push([msg[i]])
|
||||||
|
}
|
||||||
|
/** 计算页数 */
|
||||||
|
let book = count / pageSize;
|
||||||
|
if (book % 1 === 0) {
|
||||||
|
book = result;
|
||||||
|
} else {
|
||||||
|
book = Math.floor(book) + 1;
|
||||||
|
}
|
||||||
|
if (type == 'list' && msg.length >= pageSize) {
|
||||||
|
result.push(`更多内容请翻页查看\n如:#表情列表${Number(page) + 1}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = `表情列表,第${page}页,共${count}条,共${book}页`
|
||||||
|
if (type == 'search') {
|
||||||
|
title = `表情${search},${count}条`
|
||||||
|
}
|
||||||
|
|
||||||
|
let forwardMsg = await common.makeForwardMsg(this.e, [title, ...result], title)
|
||||||
|
|
||||||
|
this.e.reply(forwardMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
pagination(pageNo, pageSize, array) {
|
||||||
|
let offset = (pageNo - 1) * pageSize
|
||||||
|
return offset + pageSize >= array.length ? array.slice(offset, array.length) : array.slice(offset, offset + pageSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关键词转换成可发送消息
|
||||||
|
* @param msg
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async keyWordTran(msg) {
|
||||||
|
/** 图片 */
|
||||||
|
if (msg.includes('{image')) {
|
||||||
|
let tmp = msg.split('{image')
|
||||||
|
if (tmp.length > 2) return false
|
||||||
|
|
||||||
|
let md5 = tmp[1].replace(/}|_|:/g, '')
|
||||||
|
|
||||||
|
msg = segment.image(`http://gchat.qpic.cn/gchatpic_new/0/0-0-${md5}/0`)
|
||||||
|
msg.asface = true
|
||||||
|
} else if (msg.includes('{at:')) {
|
||||||
|
let tmp = msg.match(/{at:(.+?)}/g)
|
||||||
|
|
||||||
|
for (let qq of tmp) {
|
||||||
|
qq = qq.match(/[1-9][0-9]{4,14}/g)[0]
|
||||||
|
let member = await await this.e.bot.getGroupMemberInfo(this.group_id, Number(qq)).catch(() => { })
|
||||||
|
let name = member?.card ?? member?.nickname
|
||||||
|
if (!name) continue
|
||||||
|
msg = msg.replace(`{at:${qq}}`, `@${name}`)
|
||||||
|
}
|
||||||
|
} else if (msg.includes('{face')) {
|
||||||
|
let tmp = msg.match(/{face(:|_)(.+?)}/g)
|
||||||
|
if (!tmp) return msg
|
||||||
|
msg = []
|
||||||
|
for (let face of tmp) {
|
||||||
|
let id = face.match(/\d+/g)
|
||||||
|
msg.push(segment.face(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async faceDetail() {
|
||||||
|
if (!this.e.message) return false
|
||||||
|
this.isGlobal = false
|
||||||
|
await this.getGroupId()
|
||||||
|
if (!this.group_id) return false
|
||||||
|
let faceDetailReg = /^#+(全局)?(?:查看|查询)(?:表情|词条)(.+)$/
|
||||||
|
let regGroup = faceDetailReg.exec(this.e.msg)
|
||||||
|
let keyWord
|
||||||
|
if (regGroup[1]) {
|
||||||
|
this.isGlobal = true
|
||||||
|
}
|
||||||
|
keyWord = regGroup[2].trim()
|
||||||
|
|
||||||
|
if (keyWord === '') return
|
||||||
|
|
||||||
|
this.initTextArr()
|
||||||
|
this.initGlobalTextArr()
|
||||||
|
|
||||||
|
let faces = textArr[this.group_id].get(keyWord) || []
|
||||||
|
let globalfaces = textArr[this.e.bot.uin].get(keyWord) || []
|
||||||
|
faces = [...faces, ...globalfaces]
|
||||||
|
|
||||||
|
if (lodash.isEmpty(faces)) {
|
||||||
|
await this.e.reply(`表情${keyWord}不存在`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// process faces into replyArr in type:
|
||||||
|
let replyArr = []
|
||||||
|
for (let i = 0; i < faces.length; i++) {
|
||||||
|
let face = faces[i]
|
||||||
|
let faceItem = face[0]
|
||||||
|
let fromUser = faceItem?.from_user
|
||||||
|
if (fromUser) {
|
||||||
|
fromUser = `添加者: ${fromUser.card}(${fromUser.nickname})[${fromUser.user_id}]`
|
||||||
|
} else {
|
||||||
|
fromUser = '未知'
|
||||||
|
}
|
||||||
|
let faceContent
|
||||||
|
console.log(faceItem)
|
||||||
|
if (faceItem.type === 'image') {
|
||||||
|
// face is an image
|
||||||
|
let tmp = segment.image(faceItem.local)
|
||||||
|
tmp.asface = faceItem.asface
|
||||||
|
faceContent = tmp
|
||||||
|
replyArr.push(`${i + 1}、${fromUser}`)
|
||||||
|
replyArr.push(faceContent)
|
||||||
|
} else {
|
||||||
|
faceContent = `${faceItem.text}`
|
||||||
|
replyArr.push(`${i + 1}、${fromUser}: ` + faceContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lodash.isEmpty(replyArr)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let forwardMsg = await common.makeForwardMsg(this.e, replyArr, `表情${keyWord}详情`)
|
||||||
|
|
||||||
|
this.e.reply(forwardMsg)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
import { ConfigController as cfg } from '#miao/config'
|
||||||
|
import { plugin } from '#miao/core'
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class disFriPoke extends plugin {
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
name: '禁止私聊',
|
||||||
|
dsc: '对私聊禁用做处理当开启私聊禁用时只接收cookie以及抽卡链接',
|
||||||
|
event: 'notice.friend.poke'
|
||||||
|
})
|
||||||
|
this.priority = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
async accept() {
|
||||||
|
if (!cfg.other?.disablePrivate) return
|
||||||
|
|
||||||
|
if (this.e.isMaster) return
|
||||||
|
|
||||||
|
this.e.reply(cfg.other.disableMsg)
|
||||||
|
return 'return'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
|
||||||
|
import { ConfigController as cfg } from '#miao/config'
|
||||||
|
import { plugin } from '#miao/core'
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class disPri extends plugin {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
/**
|
||||||
|
name: '禁止私聊',
|
||||||
|
dsc: '对私聊禁用做处理当开启私聊禁用时只接收cookie以及抽卡链接',
|
||||||
|
*/
|
||||||
|
super({
|
||||||
|
event: 'message.private'
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
this.priority = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async accept() {
|
||||||
|
if (!cfg.other?.disablePrivate) return
|
||||||
|
|
||||||
|
if (this.e.isMaster) return
|
||||||
|
|
||||||
|
/** 发送日志文件,xlsx,json */
|
||||||
|
if (this.e.file) {
|
||||||
|
if (!/(.*)\.txt|xlsx|json/ig.test(this.e.file?.name)) {
|
||||||
|
this.sendTips()
|
||||||
|
return 'return'
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 绑定ck,抽卡链接 */
|
||||||
|
let wordReg = /(.*)(ltoken|_MHYUUID|authkey=)(.*)|导出记录(json)*|(记录|安卓|苹果|ck|cookie|体力)帮助|^帮助$|^#*(删除|我的)ck$|^#(我的)?(uid|UID)[0-9]{0,2}$/g
|
||||||
|
/** 自定义通行字符 */
|
||||||
|
let disableAdopt = cfg.other?.disableAdopt
|
||||||
|
if (!Array.isArray(disableAdopt)) {
|
||||||
|
disableAdopt = []
|
||||||
|
}
|
||||||
|
disableAdopt = disableAdopt.filter(str => str != null && str !== '');
|
||||||
|
let disableReg = `(.*)(${disableAdopt.join('|')})(.*)`
|
||||||
|
if (this.e.raw_message) {
|
||||||
|
if (!new RegExp(wordReg).test(this.e.raw_message) && (disableAdopt.length === 0 || !new RegExp(disableReg).test(this.e.raw_message))) {
|
||||||
|
this.sendTips()
|
||||||
|
return 'return'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async sendTips() {
|
||||||
|
/** 冷却cd 10s */
|
||||||
|
let cd = 10
|
||||||
|
|
||||||
|
if (this.e.user_id == cfg.qq) return
|
||||||
|
|
||||||
|
/** cd */
|
||||||
|
let key = `Yz:disablePrivate:${this.e.user_id}`
|
||||||
|
if (await redis.get(key)) return
|
||||||
|
|
||||||
|
this.e.reply(cfg.other.disableMsg)
|
||||||
|
|
||||||
|
redis.setEx(key, cd, '1')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { plugin } from '#miao/core'
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class example2 extends plugin {
|
||||||
|
constructor () {
|
||||||
|
/**
|
||||||
|
name: '复读',
|
||||||
|
dsc: '复读用户发送的内容,然后撤回',
|
||||||
|
*/
|
||||||
|
super({
|
||||||
|
event: 'message',
|
||||||
|
priority: 5000,
|
||||||
|
rule: [
|
||||||
|
{
|
||||||
|
reg: '^#复读$',
|
||||||
|
fnc: 'repeat'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async repeat () {
|
||||||
|
/** 设置上下文,后续接收到内容会执行doRep方法 */
|
||||||
|
this.setContext('doRep')
|
||||||
|
/** 回复 */
|
||||||
|
await this.reply('请发送要复读的内容', false, { at: true })
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 接受内容
|
||||||
|
*/
|
||||||
|
doRep () {
|
||||||
|
/** 复读内容 */
|
||||||
|
this.reply(this.e.message, false, { recallMsg: 5 })
|
||||||
|
/** 结束上下文 */
|
||||||
|
this.finish('doRep')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
|
||||||
|
import { ConfigController as cfg } from '#miao/config'
|
||||||
|
import { sleep } from '#miao/utils'
|
||||||
|
import { plugin } from '#miao/core'
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class friend extends plugin {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
name: 'autoFriend',
|
||||||
|
dsc: '自动同意好友',
|
||||||
|
*/
|
||||||
|
super({
|
||||||
|
event: 'request.friend'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async accept() {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if (this.e.sub_type == 'add' || this.e.sub_type == 'single') {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if (cfg.other.autoFriend == 1) {
|
||||||
|
logger.mark(`[自动同意][添加好友] ${this.e.user_id}`)
|
||||||
|
await sleep(2000)
|
||||||
|
this.e.approve(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
|
||||||
|
import { ConfigController as cfg } from '#miao/config'
|
||||||
|
import { plugin } from '#miao/core'
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class invite extends plugin {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
name: 'invite',
|
||||||
|
dsc: '主人邀请自动进群',
|
||||||
|
*/
|
||||||
|
super({
|
||||||
|
event: 'request.group.invite'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async accept() {
|
||||||
|
if (!cfg.masterQQ || !cfg.masterQQ.includes(String(this.e.user_id))) {
|
||||||
|
logger.mark(`[邀请加群]:${this.e.group_name}:${this.e.group_id}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.mark(`[主人邀请加群]:${this.e.group_name}:${this.e.group_id}`)
|
||||||
|
this.e.approve(true)
|
||||||
|
this.e.bot.sendPrivateMsg(this.e.user_id, `已同意加群:${this.e.group_name}`).catch((err) => {
|
||||||
|
logger.error(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { plugin, segment } from '#miao/core'
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class newcomer extends plugin {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
/**
|
||||||
|
name: '欢迎新人',
|
||||||
|
dsc: '新人入群欢迎',
|
||||||
|
*/
|
||||||
|
super({
|
||||||
|
event: 'notice.group.increase',
|
||||||
|
priority: 5000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接受到消息都会执行一次
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async accept() {
|
||||||
|
/** 定义入群欢迎内容 */
|
||||||
|
let msg = '欢迎新人!'
|
||||||
|
/** 冷却cd 30s */
|
||||||
|
let cd = 30
|
||||||
|
if (this.e.user_id == this.e.bot.uin) return
|
||||||
|
/** cd */
|
||||||
|
let key = `Yz:newcomers:${this.e.group_id}`
|
||||||
|
if (await redis.get(key)) return
|
||||||
|
redis.set(key, '1', { EX: cd })
|
||||||
|
/** 回复 */
|
||||||
|
await this.reply([
|
||||||
|
segment.at(this.e.user_id),
|
||||||
|
// segment.image(),
|
||||||
|
msg
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { plugin } from '#miao/core'
|
||||||
|
export class outNotice extends plugin {
|
||||||
|
tips = '退群了'
|
||||||
|
constructor() {
|
||||||
|
/**
|
||||||
|
name: '退群通知',
|
||||||
|
dsc: 'xx退群了',
|
||||||
|
*/
|
||||||
|
super({
|
||||||
|
event: 'notice.group.decrease'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async accept() {
|
||||||
|
if (this.e.user_id == this.e.bot.uin) return
|
||||||
|
let name = null, msg = null
|
||||||
|
if (this.e.member) {
|
||||||
|
name = this.e.member.card || this.e.member.nickname
|
||||||
|
}
|
||||||
|
if (name) {
|
||||||
|
msg = `${name}(${this.e.user_id}) ${this.tips}`
|
||||||
|
} else {
|
||||||
|
msg = `${this.e.user_id} ${this.tips}`
|
||||||
|
}
|
||||||
|
logger.mark(`[退出通知]${this.e.logText} ${msg}`)
|
||||||
|
await this.reply(msg)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { ConfigController as cfg } from '#miao/config'
|
||||||
|
import { plugin } from '#miao/core'
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class quit extends plugin {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
/**
|
||||||
|
name: 'notice',
|
||||||
|
dsc: '自动退群',
|
||||||
|
*/
|
||||||
|
super({
|
||||||
|
event: 'notice.group.increase'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async accept() {
|
||||||
|
if (this.e.user_id != this.e.bot.uin) return
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
let other = cfg.other
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if (other.autoQuit <= 0) return
|
||||||
|
/**
|
||||||
|
* 判断主人,主人邀请不退群
|
||||||
|
*/
|
||||||
|
let gl = await this.e.group.getMemberMap()
|
||||||
|
for (let qq of cfg.masterQQ) {
|
||||||
|
if (gl.has(Number(qq))) {
|
||||||
|
logger.mark(`[主人拉群] ${this.e.group_id}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 自动退群
|
||||||
|
*/
|
||||||
|
if (Array.from(gl).length <= other.autoQuit && !this.e.group.is_owner) {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
await this.e.reply('禁止拉群,已自动退出')
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
logger.mark(`[自动退群] ${this.e.group_id}`)
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
setTimeout(() => {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
this.e.group.quit()
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
import { plugin } from '#miao/core'
|
||||||
|
import fetch from 'node-fetch'
|
||||||
|
import net from 'net'
|
||||||
|
import fs from 'fs'
|
||||||
|
import YAML from 'yaml'
|
||||||
|
import { exec } from 'child_process'
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param port
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const isPortTaken = async (port) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const tester = net.createServer()
|
||||||
|
.once('error', () => resolve(true))
|
||||||
|
.once('listening', () => tester.once('close', () => resolve(false)).close())
|
||||||
|
.listen(port);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class Restart extends plugin {
|
||||||
|
key = 'Yz:restart'
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
/**
|
||||||
|
name: '重启',
|
||||||
|
dsc: '#重启',
|
||||||
|
*/
|
||||||
|
super({
|
||||||
|
event: 'message',
|
||||||
|
priority: 10,
|
||||||
|
rule: [{
|
||||||
|
reg: '^#重启$',
|
||||||
|
fnc: 'restart',
|
||||||
|
permission: 'master'
|
||||||
|
}, {
|
||||||
|
reg: '^#(停机|关机)$',
|
||||||
|
fnc: 'stop',
|
||||||
|
permission: 'master'
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
let restart = await redis.get(this.key)
|
||||||
|
if (restart) {
|
||||||
|
restart = JSON.parse(restart)
|
||||||
|
const uin = restart?.uin || Bot.uin
|
||||||
|
let time = restart.time || new Date().getTime()
|
||||||
|
time = (new Date().getTime() - time) / 1000
|
||||||
|
|
||||||
|
let msg = `重启成功:耗时${time.toFixed(2)}秒`
|
||||||
|
try {
|
||||||
|
if (restart.isGroup) {
|
||||||
|
Bot[uin].pickGroup(restart.id).sendMsg(msg)
|
||||||
|
} else {
|
||||||
|
Bot[uin].pickUser(restart.id).sendMsg(msg)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
/** 不发了,发不出去... */
|
||||||
|
logger.debug(error)
|
||||||
|
}
|
||||||
|
redis.del(this.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async restart() {
|
||||||
|
let restart_port
|
||||||
|
try {
|
||||||
|
restart_port = YAML.parse(fs.readFileSync(`./config/config/bot.yaml`, `utf-8`))
|
||||||
|
restart_port = restart_port.restart_port || 27881
|
||||||
|
} catch { }
|
||||||
|
await this.e.reply('开始执行重启,请稍等...')
|
||||||
|
logger.mark(`${this.e.logFnc} 开始执行重启,请稍等...`)
|
||||||
|
|
||||||
|
let data = JSON.stringify({
|
||||||
|
uin: this.e?.self_id || this.e.bot.uin,
|
||||||
|
isGroup: !!this.e.isGroup,
|
||||||
|
id: this.e.isGroup ? this.e.group_id : this.e.user_id,
|
||||||
|
time: new Date().getTime()
|
||||||
|
})
|
||||||
|
|
||||||
|
let npm = await this.checkPnpm()
|
||||||
|
await redis.set(this.key, data, { EX: 120 })
|
||||||
|
if (await isPortTaken(restart_port || 27881)) {
|
||||||
|
try {
|
||||||
|
let result = await fetch(`http://localhost:${restart_port || 27881}/restart`)
|
||||||
|
result = await result.text()
|
||||||
|
if (result !== `OK`) {
|
||||||
|
redis.del(this.key)
|
||||||
|
this.e.reply(`操作失败!`)
|
||||||
|
logger.error(`重启失败`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
redis.del(this.key)
|
||||||
|
this.e.reply(`操作失败!\n${error}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
let cm = `${npm} start`
|
||||||
|
if (process.argv[1].includes('pm2')) {
|
||||||
|
cm = `${npm} run restart`
|
||||||
|
}
|
||||||
|
|
||||||
|
exec(cm, { windowsHide: true }, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
redis.del(this.key)
|
||||||
|
this.e.reply(`操作失败!\n${error.stack}`)
|
||||||
|
logger.error(`重启失败\n${error.stack}`)
|
||||||
|
} else if (stdout) {
|
||||||
|
logger.mark('重启成功,运行已由前台转为后台')
|
||||||
|
logger.mark(`查看日志请用命令:${npm} run log`)
|
||||||
|
logger.mark(`停止后台运行命令:${npm} stop`)
|
||||||
|
process.exit()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
redis.del(this.key)
|
||||||
|
let e = error.stack ?? error
|
||||||
|
this.e.reply(`操作失败!\n${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkPnpm() {
|
||||||
|
let npm = 'npm'
|
||||||
|
let ret = await this.execSync('pnpm -v')
|
||||||
|
if (ret.stdout) npm = 'pnpm'
|
||||||
|
return npm
|
||||||
|
}
|
||||||
|
|
||||||
|
async execSync(cmd) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
exec(cmd, { windowsHide: true }, (error, stdout, stderr) => {
|
||||||
|
resolve({ error, stdout, stderr })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async stop() {
|
||||||
|
let restart_port
|
||||||
|
try {
|
||||||
|
restart_port = YAML.parse(fs.readFileSync(`./config/config/bot.yaml`, `utf-8`))
|
||||||
|
restart_port = restart_port.restart_port || 27881
|
||||||
|
} catch { }
|
||||||
|
if (await isPortTaken(restart_port || 27881)) {
|
||||||
|
try {
|
||||||
|
logger.mark('关机成功,已停止运行')
|
||||||
|
await this.e.reply(`关机成功,已停止运行`)
|
||||||
|
await fetch(`http://localhost:${restart_port || 27881}/exit`)
|
||||||
|
return
|
||||||
|
} catch (error) {
|
||||||
|
this.e.reply(`操作失败!\n${error}`)
|
||||||
|
logger.error(`关机失败\n${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!process.argv[1].includes('pm2')) {
|
||||||
|
logger.mark('关机成功,已停止运行')
|
||||||
|
await this.e.reply('关机成功,已停止运行')
|
||||||
|
process.exit()
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.mark('关机成功,已停止运行')
|
||||||
|
await this.e.reply('关机成功,已停止运行')
|
||||||
|
|
||||||
|
let npm = await this.checkPnpm()
|
||||||
|
exec(`${npm} stop`, { windowsHide: true }, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
this.e.reply(`操作失败!\n${error.stack}`)
|
||||||
|
logger.error(`关机失败\n${error.stack}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { plugin } from '#miao/core'
|
||||||
|
import {makeForwardMsg} from '#miao/core'
|
||||||
|
import fs from "node:fs"
|
||||||
|
import lodash from "lodash"
|
||||||
|
import moment from "moment"
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class sendLog extends plugin {
|
||||||
|
lineNum = 100
|
||||||
|
maxNum = 1000
|
||||||
|
errFile = "logs/error.log"
|
||||||
|
logFile = `logs/command.${moment().format("YYYY-MM-DD")}.log`
|
||||||
|
constructor() {
|
||||||
|
/**
|
||||||
|
name: "发送日志",
|
||||||
|
dsc: "发送最近100条运行日志",
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
super({
|
||||||
|
event: "message",
|
||||||
|
rule: [
|
||||||
|
{
|
||||||
|
reg: "^#(运行|错误)*日志[0-9]*(.*)",
|
||||||
|
fnc: "sendLog",
|
||||||
|
permission: "master"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async sendLog() {
|
||||||
|
let lineNum = this.e.msg.match(/\d+/g)
|
||||||
|
if (lineNum) {
|
||||||
|
this.lineNum = lineNum[0]
|
||||||
|
} else {
|
||||||
|
this.keyWord = this.e.msg.replace(/#|运行|错误|日志|\d/g, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
let logFile = this.logFile
|
||||||
|
let type = "运行"
|
||||||
|
if (this.e.msg.includes("错误")) {
|
||||||
|
logFile = this.errFile
|
||||||
|
type = "错误"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.keyWord) type = this.keyWord
|
||||||
|
|
||||||
|
const log = this.getLog(logFile)
|
||||||
|
|
||||||
|
if (lodash.isEmpty(log))
|
||||||
|
return this.reply(`暂无相关日志:${type}`)
|
||||||
|
|
||||||
|
return this.reply(await makeForwardMsg(this.e, [log.join("\n")], `最近${log.length}条${type}日志`))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param logFile
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getLog(logFile) {
|
||||||
|
let log = fs.readFileSync(logFile, { encoding: "utf-8" })
|
||||||
|
log = log.split("\n")
|
||||||
|
|
||||||
|
if (this.keyWord) {
|
||||||
|
for (const i in log)
|
||||||
|
if (!log[i].includes(this.keyWord))
|
||||||
|
delete log[i]
|
||||||
|
} else {
|
||||||
|
log = lodash.slice(log, (Number(this.lineNum) + 1) * -1)
|
||||||
|
}
|
||||||
|
log = log.reverse()
|
||||||
|
|
||||||
|
const tmp = []
|
||||||
|
for (let i of log) {
|
||||||
|
if (!i) continue
|
||||||
|
if (this.keyWord && tmp.length >= this.maxNum) return
|
||||||
|
/* eslint-disable no-control-regex */
|
||||||
|
i = i.replace(/\x1b[[0-9;]*m/g, "")
|
||||||
|
i = i.replace(/\r|\n/, "")
|
||||||
|
tmp.push(i)
|
||||||
|
}
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
|
||||||
|
import { ConfigController as cfg } from '#miao/config'
|
||||||
|
import moment from 'moment'
|
||||||
|
import { plugin } from '#miao/core'
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export class status extends plugin {
|
||||||
|
/**
|
||||||
|
name: '其他功能',
|
||||||
|
dsc: '#状态',
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super({
|
||||||
|
event: 'message',
|
||||||
|
rule: [
|
||||||
|
{
|
||||||
|
reg: '^#状态$',
|
||||||
|
fnc: 'status'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async status() {
|
||||||
|
if (this.e.isMaster) return this.statusMaster()
|
||||||
|
|
||||||
|
if (!this.e.isGroup) {
|
||||||
|
this.reply('请群聊查看')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.statusGroup()
|
||||||
|
}
|
||||||
|
|
||||||
|
async statusMaster() {
|
||||||
|
let runTime = moment().diff(moment.unix(this.e.bot.stat.start_time), 'seconds')
|
||||||
|
let Day = Math.floor(runTime / 3600 / 24)
|
||||||
|
let Hour = Math.floor((runTime / 3600) % 24)
|
||||||
|
let Min = Math.floor((runTime / 60) % 60)
|
||||||
|
if (Day > 0) {
|
||||||
|
runTime = `${Day}天${Hour}小时${Min}分钟`
|
||||||
|
} else {
|
||||||
|
runTime = `${Hour}小时${Min}分钟`
|
||||||
|
}
|
||||||
|
|
||||||
|
let format = (bytes) => {
|
||||||
|
return (bytes / 1024 / 1024).toFixed(2) + 'MB'
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = '-------状态-------'
|
||||||
|
msg += `\n运行时间:${runTime}`
|
||||||
|
msg += `\n内存使用:${format(process.memoryUsage().rss)}`
|
||||||
|
msg += `\n当前版本:v${cfg.package.version}`
|
||||||
|
msg += '\n-------累计-------'
|
||||||
|
msg += await this.getCount()
|
||||||
|
|
||||||
|
await this.reply(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
async statusGroup() {
|
||||||
|
let msg = '-------状态-------'
|
||||||
|
msg += await this.getCount(this.e.group_id)
|
||||||
|
|
||||||
|
await this.reply(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCount(groupId = '') {
|
||||||
|
this.date = moment().format('MMDD')
|
||||||
|
this.month = Number(moment().month()) + 1
|
||||||
|
|
||||||
|
this.key = 'Yz:count:'
|
||||||
|
|
||||||
|
if (groupId) {
|
||||||
|
this.key += `group:${groupId}:`
|
||||||
|
}
|
||||||
|
|
||||||
|
this.msgKey = {
|
||||||
|
day: `${this.key}sendMsg:day:`,
|
||||||
|
month: `${this.key}sendMsg:month:`
|
||||||
|
}
|
||||||
|
|
||||||
|
this.screenshotKey = {
|
||||||
|
day: `${this.key}screenshot:day:`,
|
||||||
|
month: `${this.key}screenshot:month:`
|
||||||
|
}
|
||||||
|
|
||||||
|
let week = {
|
||||||
|
msg: 0,
|
||||||
|
screenshot: 0
|
||||||
|
}
|
||||||
|
for (let i = 0; i <= 6; i++) {
|
||||||
|
let date = moment().startOf('week').add(i, 'days').format('MMDD')
|
||||||
|
|
||||||
|
week.msg += Number(await redis.get(`${this.msgKey.day}${date}`)) ?? 0
|
||||||
|
week.screenshot += Number(await redis.get(`${this.screenshotKey.day}${date}`)) ?? 0
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = {
|
||||||
|
total: {
|
||||||
|
msg: await redis.get(`${this.key}sendMsg:total`) || 0,
|
||||||
|
screenshot: await redis.get(`${this.key}screenshot:total`) || 0
|
||||||
|
},
|
||||||
|
today: {
|
||||||
|
msg: await redis.get(`${this.msgKey.day}${this.date}`) || 0,
|
||||||
|
screenshot: await redis.get(`${this.screenshotKey.day}${this.date}`) || 0
|
||||||
|
},
|
||||||
|
week,
|
||||||
|
month: {
|
||||||
|
msg: await redis.get(`${this.msgKey.month}${this.month}`) || 0,
|
||||||
|
screenshot: await redis.get(`${this.screenshotKey.month}${this.month}`) || 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = ''
|
||||||
|
if (groupId) {
|
||||||
|
msg = `\n发送消息:${count.today.msg}条`
|
||||||
|
msg += `\n生成图片:${count.today.screenshot}次`
|
||||||
|
} else {
|
||||||
|
msg = `\n发送消息:${count.total.msg}条`
|
||||||
|
msg += `\n生成图片:${count.total.screenshot}次`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count.month.msg > 200) {
|
||||||
|
msg += '\n-------本周-------'
|
||||||
|
msg += `\n发送消息:${count.week.msg}条`
|
||||||
|
msg += `\n生成图片:${count.week.screenshot}次`
|
||||||
|
}
|
||||||
|
if (moment().format('D') >= 8 && count.month.msg > 400) {
|
||||||
|
msg += '\n-------本月-------'
|
||||||
|
msg += `\n发送消息:${count.month.msg}条`
|
||||||
|
msg += `\n生成图片:${count.month.screenshot}次`
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,266 @@
|
||||||
|
import { makeForwardMsg, plugin } from '#miao/core'
|
||||||
|
import lodash from 'lodash'
|
||||||
|
import fs from 'node:fs'
|
||||||
|
import { Restart } from './restart.js'
|
||||||
|
import {} from '#miao/core'
|
||||||
|
import { sleep } from '#miao/utils'
|
||||||
|
import { exec, execSync } from 'child_process'
|
||||||
|
import { BOT_NAME } from '#miao/config'
|
||||||
|
|
||||||
|
let uping = false
|
||||||
|
|
||||||
|
export class update extends plugin {
|
||||||
|
typeName = BOT_NAME
|
||||||
|
messages = []
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
/**
|
||||||
|
name: '更新',
|
||||||
|
dsc: '#更新 #强制更新',
|
||||||
|
*/
|
||||||
|
super({
|
||||||
|
event: 'message',
|
||||||
|
priority: 4000,
|
||||||
|
rule: [
|
||||||
|
{
|
||||||
|
reg: '^#更新日志',
|
||||||
|
fnc: 'updateLog'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: '^#(强制)?更新',
|
||||||
|
fnc: 'update'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: '^#(静默)?全部(强制)?更新$',
|
||||||
|
fnc: 'updateAll',
|
||||||
|
permission: 'master'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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 sleep(1500)
|
||||||
|
await this.runUpdate(plu)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testReg) {
|
||||||
|
await this.reply(await 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 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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# 确保脚本抛出遇到的错误
|
||||||
|
set -e
|
||||||
|
|
||||||
|
git init
|
||||||
|
git add -A
|
||||||
|
git commit -m 'update: 修改'
|
||||||
|
|
||||||
|
git push -f git@github.com:yoimiya-kokomi/Miao-Yunzai.git master:system
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"name": "system-plugin",
|
||||||
|
"version": "1.0.0-rc.0",
|
||||||
|
"author": "Yoimiya-Kokomi, Le-niao",
|
||||||
|
"description": "QQ Group Bot",
|
||||||
|
"main": "./index.js",
|
||||||
|
"private":true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {}
|
||||||
|
}
|
Loading…
Reference in New Issue