2023-10-24 19:34:36 +00:00
|
|
|
|
import fs from 'node:fs'
|
2022-07-30 21:06:00 +00:00
|
|
|
|
import { promisify } from 'util'
|
|
|
|
|
import { pipeline } from 'stream'
|
|
|
|
|
import MD5 from 'md5'
|
|
|
|
|
import fetch from 'node-fetch'
|
|
|
|
|
import lodash from 'lodash'
|
2023-03-07 17:52:11 +00:00
|
|
|
|
import { Cfg, Data } from '#miao'
|
|
|
|
|
import { Character } from '#miao.models'
|
2024-05-21 18:05:37 +00:00
|
|
|
|
import { miaoPath } from '#miao.path'
|
2022-06-25 13:56:05 +00:00
|
|
|
|
|
2024-05-21 18:05:37 +00:00
|
|
|
|
const resPath = miaoPath + '/resources/'
|
2022-07-30 21:06:00 +00:00
|
|
|
|
let regex = /^#?\s*(?:喵喵)?(?:上传|添加)(.+)(?:照片|写真|图片|图像)\s*$/
|
2022-12-03 19:45:50 +00:00
|
|
|
|
let profileRegex = /^#?\s*(?:喵喵)?(?:上传|添加)(.+)(?:面板图)\s*$/
|
2022-12-03 21:17:07 +00:00
|
|
|
|
let isProfile = false
|
|
|
|
|
|
|
|
|
|
export async function uploadCharacterImg (e) {
|
2022-07-30 21:06:00 +00:00
|
|
|
|
let promise = await isAllowedToUploadCharacterImage(e)
|
2022-06-25 13:56:05 +00:00
|
|
|
|
if (!promise) {
|
2022-09-30 12:20:04 +00:00
|
|
|
|
return false
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-30 21:06:00 +00:00
|
|
|
|
let imageMessages = []
|
|
|
|
|
let msg = e.msg
|
|
|
|
|
let regRet = regex.exec(msg)
|
2022-12-03 19:45:50 +00:00
|
|
|
|
if (msg.includes('面板')) {
|
2022-12-03 21:17:07 +00:00
|
|
|
|
isProfile = true
|
2022-12-03 19:45:50 +00:00
|
|
|
|
regRet = profileRegex.exec(msg)
|
2023-03-30 01:41:38 +00:00
|
|
|
|
} else {
|
|
|
|
|
isProfile = false
|
2022-12-03 19:45:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-30 21:06:00 +00:00
|
|
|
|
// 通过解析正则获取消息中的角色名
|
2022-06-27 20:46:49 +00:00
|
|
|
|
if (!regRet || !regRet[1]) {
|
2022-09-30 12:20:04 +00:00
|
|
|
|
return false
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2022-07-30 21:06:00 +00:00
|
|
|
|
let char = Character.get(regRet[1])
|
2022-06-27 20:46:49 +00:00
|
|
|
|
if (!char || !char.name) {
|
2022-09-30 12:20:04 +00:00
|
|
|
|
return false
|
2022-06-27 20:46:49 +00:00
|
|
|
|
}
|
2022-07-30 21:06:00 +00:00
|
|
|
|
let name = char.name
|
2022-06-25 13:56:05 +00:00
|
|
|
|
for (let val of e.message) {
|
2022-07-30 21:06:00 +00:00
|
|
|
|
if (val.type === 'image') {
|
|
|
|
|
imageMessages.push(val)
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-20 00:31:09 +00:00
|
|
|
|
if (imageMessages.length === 0) {
|
2022-10-26 20:28:44 +00:00
|
|
|
|
let source
|
2023-07-20 00:31:09 +00:00
|
|
|
|
if (e.getReply) {
|
|
|
|
|
source = await e.getReply()
|
|
|
|
|
} else if (e.source) {
|
|
|
|
|
if (e.group?.getChatHistory) {
|
|
|
|
|
// 支持at图片添加,以及支持后发送
|
|
|
|
|
source = (await e.group.getChatHistory(e.source?.seq, 1)).pop()
|
|
|
|
|
} else if (e.friend?.getChatHistory) {
|
|
|
|
|
source = (await e.friend.getChatHistory((e.source?.time + 1), 1)).pop()
|
|
|
|
|
}
|
2022-10-26 20:28:44 +00:00
|
|
|
|
}
|
|
|
|
|
if (source) {
|
|
|
|
|
for (let val of source.message) {
|
|
|
|
|
if (val.type === 'image') {
|
|
|
|
|
imageMessages.push(val)
|
2023-03-08 06:19:14 +00:00
|
|
|
|
} else if (val.type === 'xml' || val.type === 'forward') {// 支持合并转发消息内置的图片批量上传,喵喵 喵喵喵? 喵喵喵喵
|
|
|
|
|
let resid
|
2023-12-19 18:56:18 +00:00
|
|
|
|
try {
|
|
|
|
|
resid = val.data.match(/m_resid="(\d|\w|\/|\+)*"/)[0].replace(/m_resid=|"/g, '')
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.log('Miao合并上传:转换id获取')
|
|
|
|
|
resid = val.id
|
|
|
|
|
}
|
2022-12-03 21:17:07 +00:00
|
|
|
|
if (!resid) break
|
2023-07-20 00:31:09 +00:00
|
|
|
|
let message = await e.bot.getForwardMsg(resid)
|
2022-12-03 19:45:50 +00:00
|
|
|
|
for (const item of message) {
|
|
|
|
|
for (const i of item.message) {
|
|
|
|
|
if (i.type === 'image') {
|
|
|
|
|
imageMessages.push(i)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-26 20:28:44 +00:00
|
|
|
|
}
|
2022-09-12 19:17:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-25 13:56:05 +00:00
|
|
|
|
if (imageMessages.length <= 0) {
|
2022-09-23 21:59:30 +00:00
|
|
|
|
e.reply('消息中未找到图片,请将要发送的图片与消息一同发送或引用要添加的图像..')
|
2022-07-30 21:06:00 +00:00
|
|
|
|
return true
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2022-07-30 21:06:00 +00:00
|
|
|
|
await saveImages(e, name, imageMessages)
|
|
|
|
|
return true
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-03 21:17:07 +00:00
|
|
|
|
async function saveImages (e, name, imageMessages) {
|
2022-08-24 01:07:06 +00:00
|
|
|
|
let imgMaxSize = e?.groupConfig?.imgMaxSize || 5
|
2022-07-30 21:06:00 +00:00
|
|
|
|
let pathSuffix = `character-img/${name}/upload`
|
2022-12-03 21:17:07 +00:00
|
|
|
|
if (isProfile) pathSuffix = `profile/normal-character/${name}`
|
2022-07-30 21:06:00 +00:00
|
|
|
|
let path = resPath + pathSuffix
|
2022-06-25 13:56:05 +00:00
|
|
|
|
|
|
|
|
|
if (!fs.existsSync(path)) {
|
2023-12-19 18:56:18 +00:00
|
|
|
|
Data.createDir('resources/' + pathSuffix, 'miao')
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2022-07-30 21:06:00 +00:00
|
|
|
|
let senderName = lodash.truncate(e.sender.card, { length: 8 })
|
|
|
|
|
let imgCount = 0
|
2022-09-23 21:59:30 +00:00
|
|
|
|
let urlMap = {}
|
2022-06-25 13:56:05 +00:00
|
|
|
|
for (let val of imageMessages) {
|
2022-09-23 21:59:30 +00:00
|
|
|
|
if (!val.url || urlMap[val.url]) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
urlMap[val.url] = true
|
2022-07-30 21:06:00 +00:00
|
|
|
|
const response = await fetch(val.url)
|
2022-06-25 13:56:05 +00:00
|
|
|
|
if (!response.ok) {
|
2022-07-30 21:06:00 +00:00
|
|
|
|
e.reply('图片下载失败。')
|
|
|
|
|
return true
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2022-07-30 21:06:00 +00:00
|
|
|
|
if (response.headers.get('size') > 1024 * 1024 * imgMaxSize) {
|
|
|
|
|
e.reply([segment.at(e.user_id, senderName), '添加失败:图片太大了。'])
|
|
|
|
|
return true
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2023-12-19 18:56:18 +00:00
|
|
|
|
let fileName = ''
|
|
|
|
|
let fileType = 'png'
|
2023-07-20 00:31:09 +00:00
|
|
|
|
if (val.file) {
|
|
|
|
|
fileName = val.file.substring(0, val.file.lastIndexOf('.'))
|
|
|
|
|
fileType = val.file.substring(val.file.lastIndexOf('.') + 1)
|
|
|
|
|
}
|
2022-07-30 21:06:00 +00:00
|
|
|
|
if (response.headers.get('content-type') === 'image/gif') {
|
|
|
|
|
fileType = 'gif'
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2023-12-19 18:56:18 +00:00
|
|
|
|
|
|
|
|
|
if (isProfile) {
|
|
|
|
|
// 面板图默认webp
|
|
|
|
|
fileType = 'webp'
|
|
|
|
|
} else if (!'jpg,jpeg,png,webp'.split(',').includes(fileType)) {
|
|
|
|
|
// 角色图像默认jpg
|
|
|
|
|
fileType = 'jpg'
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-30 21:06:00 +00:00
|
|
|
|
let imgPath = `${path}/${fileName}.${fileType}`
|
|
|
|
|
const streamPipeline = promisify(pipeline)
|
|
|
|
|
await streamPipeline(response.body, fs.createWriteStream(imgPath))
|
2022-06-27 20:46:49 +00:00
|
|
|
|
|
|
|
|
|
// 使用md5作为文件名
|
2022-07-30 21:06:00 +00:00
|
|
|
|
let buffers = fs.readFileSync(imgPath)
|
|
|
|
|
let base64 = Buffer.from(buffers, 'base64').toString()
|
|
|
|
|
let md5 = MD5(base64)
|
2022-06-27 20:46:49 +00:00
|
|
|
|
let newImgPath = `${path}/${md5}.${fileType}`
|
|
|
|
|
if (fs.existsSync(newImgPath)) {
|
|
|
|
|
fs.unlink(newImgPath, (err) => {
|
2022-07-30 21:06:00 +00:00
|
|
|
|
console.log('unlink', err)
|
|
|
|
|
})
|
2022-06-27 20:46:49 +00:00
|
|
|
|
}
|
2022-09-23 21:59:30 +00:00
|
|
|
|
fs.rename(imgPath, newImgPath, () => {
|
2022-06-27 20:46:49 +00:00
|
|
|
|
})
|
2022-07-30 21:06:00 +00:00
|
|
|
|
imgCount++
|
2023-07-20 00:31:09 +00:00
|
|
|
|
Bot.logger.mark(`添加成功: ${newImgPath}`)
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2023-03-25 07:42:16 +00:00
|
|
|
|
e.reply([segment.at(e.user_id, senderName), `\n成功添加${imgCount}张${name}${isProfile ? '面板图' : '图片'}。`])
|
2022-07-30 21:06:00 +00:00
|
|
|
|
return true
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-03 21:17:07 +00:00
|
|
|
|
async function isAllowedToUploadCharacterImage (e) {
|
2022-12-04 18:58:05 +00:00
|
|
|
|
let sendMsg = /上传|添加/.test(e.msg) ? '添加' : '删除'
|
2022-06-25 13:56:05 +00:00
|
|
|
|
if (!e.message) {
|
2022-07-30 21:06:00 +00:00
|
|
|
|
return false
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
|
|
|
|
if (!e.msg) {
|
2022-07-30 21:06:00 +00:00
|
|
|
|
return false
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2022-10-30 18:22:07 +00:00
|
|
|
|
// master直接返回true
|
|
|
|
|
if (e.isMaster) {
|
2022-07-30 21:06:00 +00:00
|
|
|
|
return true
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2022-10-30 18:22:07 +00:00
|
|
|
|
if (e.isPrivate) {
|
2022-12-03 19:45:50 +00:00
|
|
|
|
e.reply(`只有主人才能${sendMsg}...`)
|
2022-10-30 18:22:07 +00:00
|
|
|
|
return false
|
|
|
|
|
}
|
2022-07-30 21:06:00 +00:00
|
|
|
|
let groupId = e.group_id
|
|
|
|
|
if (!groupId) {
|
|
|
|
|
return false
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2022-10-30 18:22:07 +00:00
|
|
|
|
const addLimit = e.groupConfig?.imgAddLimit || 2
|
|
|
|
|
const isAdmin = ['owner', 'admin'].includes(e.sender.role)
|
|
|
|
|
if (addLimit === 2) {
|
2022-12-03 19:45:50 +00:00
|
|
|
|
e.reply(`只有主人才能${sendMsg}...`)
|
2022-10-30 18:22:07 +00:00
|
|
|
|
return false
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2022-10-30 18:22:07 +00:00
|
|
|
|
if (addLimit === 1 && !isAdmin) {
|
2022-12-03 19:45:50 +00:00
|
|
|
|
e.reply(`只有管理员才能${sendMsg}...`)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
2022-12-03 21:17:07 +00:00
|
|
|
|
|
|
|
|
|
// 仅支持面板图删除
|
|
|
|
|
export async function delProfileImg (e) {
|
2022-12-03 19:45:50 +00:00
|
|
|
|
let promise = await isAllowedToUploadCharacterImage(e)
|
|
|
|
|
if (!promise) {
|
2022-10-30 18:22:07 +00:00
|
|
|
|
return false
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2022-12-03 19:45:50 +00:00
|
|
|
|
let char = Character.get(e.msg.replace(/#|面板图|列表|上传|删除|\d+/g, '').trim())
|
|
|
|
|
if (!char || !char.name) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
let name = char.name
|
2022-12-03 21:17:07 +00:00
|
|
|
|
let pathSuffix = `profile/normal-character/${name}`
|
2022-12-03 19:45:50 +00:00
|
|
|
|
let path = resPath + pathSuffix
|
|
|
|
|
let num = e.msg.match(/\d+/)
|
|
|
|
|
if (!num) {
|
|
|
|
|
e.reply(`删除哪张捏?请输入数字序列号,可输入【#${name}面板图列表】查看序列号`)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
try {
|
2022-12-03 21:17:07 +00:00
|
|
|
|
let imgs = fs.readdirSync(`${path}`).filter((file) => {
|
|
|
|
|
return /\.(png|webp)$/.test(file)
|
|
|
|
|
})
|
|
|
|
|
fs.unlinkSync(`${path}/${imgs[num - 1]}`)
|
2022-12-03 19:45:50 +00:00
|
|
|
|
e.reply('删除成功')
|
|
|
|
|
} catch (err) {
|
|
|
|
|
e.reply('删除失败,请检查序列号是否正确')
|
|
|
|
|
}
|
2022-07-30 21:06:00 +00:00
|
|
|
|
return true
|
2022-06-25 13:56:05 +00:00
|
|
|
|
}
|
2022-12-03 19:45:50 +00:00
|
|
|
|
|
2022-12-03 21:17:07 +00:00
|
|
|
|
export async function profileImgList (e) {
|
2022-12-03 19:45:50 +00:00
|
|
|
|
let msglist = []
|
|
|
|
|
let char = Character.get(e.msg.replace(/#|面板图列表/g, ''))
|
|
|
|
|
if (!char || !char.name) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2023-03-01 00:38:13 +00:00
|
|
|
|
if ([1, 0].includes(Cfg.get('originalPic') * 1)) {
|
|
|
|
|
e.reply('已禁止获取面板图列表')
|
2023-03-08 21:29:30 +00:00
|
|
|
|
return true
|
2023-03-01 00:38:13 +00:00
|
|
|
|
}
|
2022-12-03 19:45:50 +00:00
|
|
|
|
let name = char.name
|
2022-12-03 21:17:07 +00:00
|
|
|
|
let pathSuffix = `profile/normal-character/${name}`
|
2022-12-03 19:45:50 +00:00
|
|
|
|
let path = resPath + pathSuffix
|
2022-12-03 21:17:07 +00:00
|
|
|
|
if (!fs.existsSync(path)) {
|
|
|
|
|
e.reply(`暂无${char.name}的角色面板图`)
|
|
|
|
|
return true
|
|
|
|
|
}
|
2022-12-03 19:45:50 +00:00
|
|
|
|
try {
|
2022-12-03 21:17:07 +00:00
|
|
|
|
let imgs = fs.readdirSync(`${path}`).filter((file) => {
|
|
|
|
|
return /\.(png|webp)$/.test(file)
|
|
|
|
|
})
|
2022-12-03 19:45:50 +00:00
|
|
|
|
msglist.push({
|
2022-12-03 21:17:07 +00:00
|
|
|
|
message: [`当前查看的是${name}面板图,共${imgs.length}张,可输入【#删除${name}面板图(序列号)】进行删除`],
|
2022-12-03 19:45:50 +00:00
|
|
|
|
})
|
2022-12-03 21:17:07 +00:00
|
|
|
|
for (let i = 0; i < imgs.length; i++) {
|
|
|
|
|
// 合并转发最多99? 但是我感觉不会有这么多先不做处理
|
|
|
|
|
console.log(`${path}${imgs[i]}`)
|
2022-12-03 19:45:50 +00:00
|
|
|
|
msglist.push({
|
2023-03-28 04:55:36 +00:00
|
|
|
|
message: [`${i + 1}.`, segment.image(`file://${path}/${imgs[i]}`)],
|
2022-12-03 19:45:50 +00:00
|
|
|
|
})
|
|
|
|
|
}
|
2023-07-20 00:31:09 +00:00
|
|
|
|
let msg
|
|
|
|
|
if (e.group?.makeForwardMsg) {
|
|
|
|
|
msg = await e.group.makeForwardMsg(msglist)
|
|
|
|
|
} else if (e.friend?.makeForwardMsg) {
|
|
|
|
|
msg = await e.friend.makeForwardMsg(msglist)
|
|
|
|
|
} else {
|
|
|
|
|
msg = await Bot.makeForwardMsg(msglist)
|
|
|
|
|
}
|
|
|
|
|
let msgRsg = await e.reply(msg)
|
2022-12-03 19:45:50 +00:00
|
|
|
|
if (!msgRsg) e.reply('风控了,可私聊查看', true)
|
|
|
|
|
} catch (err) {
|
2022-12-03 21:17:07 +00:00
|
|
|
|
logger.error(err)
|
|
|
|
|
e.reply(`暂无${char.name}的角色面板图~`)
|
2022-12-03 19:45:50 +00:00
|
|
|
|
}
|
2022-12-03 21:17:07 +00:00
|
|
|
|
return true
|
|
|
|
|
}
|