尝试兼容napcat,修复伪人发送表情乱码 以及一些兼容性修改

This commit is contained in:
chenyuxin221 2025-04-03 15:38:23 +08:00
parent c82c137596
commit b4032881da
4 changed files with 134 additions and 28 deletions

View file

@ -465,6 +465,17 @@ export const faceMapReverse = {
} }
export async function convertFaces (msg, handleAt = false, e) { export async function convertFaces (msg, handleAt = false, e) {
if (!msg) return []
// 处理HTML实体编码和多余右括号
if (typeof msg === 'string') {
msg = msg.replace(/[/g, '[')
.replace(/]/g, ']')
.replace(/&/g, '&')
.replace(/\\/g, '') // 移除多余的反斜杠
.replace(/\]+/g, ']') // 将多个连续的右括号替换为单个右括号
}
handleAt = e?.isGroup && handleAt handleAt = e?.isGroup && handleAt
let groupMembers let groupMembers
let groupCardQQMap = {} let groupCardQQMap = {}
@ -486,6 +497,7 @@ export async function convertFaces (msg, handleAt = false, e) {
} }
} }
} }
let tmpMsg = '' let tmpMsg = ''
let tmpFace = '' let tmpFace = ''
let tmpAt = '' let tmpAt = ''
@ -493,13 +505,20 @@ export async function convertFaces (msg, handleAt = false, e) {
let foundAt = false let foundAt = false
let msgs = [] let msgs = []
for (let i = 0; i < msg.length; i++) { for (let i = 0; i < msg.length; i++) {
// console.log(msg[i]) const char = msg[i]
if (msg[i] === '[') { const nextChar = msg[i + 1]
if (char === '[') {
foundFace = true foundFace = true
if (tmpMsg) {
msgs.push(tmpMsg)
tmpMsg = ''
}
continue continue
} }
if (!foundFace) { if (!foundFace) {
if (handleAt && msg[i] === '@') { if (handleAt && char === '@') {
foundAt = true foundAt = true
if (tmpMsg) { if (tmpMsg) {
msgs.push(tmpMsg) msgs.push(tmpMsg)
@ -508,7 +527,7 @@ export async function convertFaces (msg, handleAt = false, e) {
continue continue
} }
if (handleAt && foundAt) { if (handleAt && foundAt) {
tmpAt += msg[i] tmpAt += char
if (groupCardQQMap[tmpAt]) { if (groupCardQQMap[tmpAt]) {
foundAt = false foundAt = false
msgs.push(segment.at(groupCardQQMap[tmpAt], groupMembers.get(groupCardQQMap[tmpAt]).card, false)) msgs.push(segment.at(groupCardQQMap[tmpAt], groupMembers.get(groupCardQQMap[tmpAt]).card, false))
@ -516,36 +535,53 @@ export async function convertFaces (msg, handleAt = false, e) {
continue continue
} }
} else { } else {
tmpMsg += msg[i] tmpMsg += char
} }
} else { } else {
if (msg[i] !== ']') { if (char !== ']') {
tmpFace += msg[i] tmpFace += char
} else { } else {
foundFace = false foundFace = false
if (faceMapReverse[tmpFace] || faceMapReverse['/' + tmpFace] || faceMapReverse[_.trimStart(tmpFace, '/')]) { // 处理CQ码格式
if (tmpMsg) { if (tmpFace.startsWith('CQ:face,id=')) {
msgs.push(tmpMsg) const faceId = parseInt(tmpFace.split('=')[1].split(',')[0])
} msgs.push(segment.face(faceId))
msgs.push(segment.face(parseInt(faceMapReverse[tmpFace] || faceMapReverse['/' + tmpFace] || faceMapReverse[_.trimStart(tmpFace, '/')]))) tmpFace = ''
tmpMsg = '' }
} else { // 处理表情名称格式
tmpMsg += `[${tmpFace}]` else if (faceMapReverse[tmpFace] || faceMapReverse['/' + tmpFace] || faceMapReverse[_.trimStart(tmpFace, '/')]) {
msgs.push(segment.face(parseInt(faceMapReverse[tmpFace] || faceMapReverse['/' + tmpFace] || faceMapReverse[_.trimStart(tmpFace, '/')])))
tmpFace = ''
} else {
// 如果找不到对应的表情,将整个内容作为普通文本处理
tmpMsg += `[${tmpFace}]`
tmpFace = ''
} }
tmpFace = ''
} }
} }
} }
// 处理剩余内容
if (tmpMsg) { if (tmpMsg) {
msgs.push(tmpMsg) msgs.push(tmpMsg)
tmpMsg = '' // 清空临时消息
} }
// 处理未闭合的表情标记
if (tmpFace) { if (tmpFace) {
msgs.push(`[${tmpFace}`) if (tmpFace.endsWith(']')) {
msgs.push(`[${tmpFace.slice(0, -1)}]`) // 如果有多余的右括号,去掉它
} else {
msgs.push(`[${tmpFace}]`) // 正常添加右括号
}
} }
if (handleAt && tmpAt) { if (handleAt && tmpAt) {
msgs.push(`@${tmpAt}`) msgs.push(`@${tmpAt}`)
} }
return msgs
// 确保返回数组
return Array.isArray(msgs) ? msgs : [msgs].filter(Boolean)
} }
export function testConvertFaces () { export function testConvertFaces () {
@ -557,4 +593,4 @@ export function testConvertFaces () {
}) })
} }
// testConvertFaces() // testConvertFaces()

View file

@ -23,15 +23,85 @@ export class SerpImageTool extends AbstractTool {
func = async function (opts) { func = async function (opts) {
let { q, limit = 2, source = 'bing' } = opts let { q, limit = 2, source = 'bing' } = opts
let serpRes = await fetch(`https://serp.ikechan8370.com/image/${source}?q=${encodeURIComponent(q)}&limit=${limit}`, {
headers: { // 验证参数
'X-From-Library': 'ikechan8370' if (!q || typeof q !== 'string') {
return {
error: 'Invalid search query',
data: []
}
}
// 验证搜索源
if (!['bing', 'yandex'].includes(source)) {
return {
error: 'Invalid search source. Must be either "bing" or "yandex"',
data: []
}
}
try {
// 构建 URL
const url = `https://serp.ikechan8370.com/image/${source}?q=${encodeURIComponent(q)}&limit=${limit}`
console.log('Searching images with URL:', url)
let serpRes = await fetch(url, {
headers: {
'X-From-Library': 'ikechan8370',
'Accept': 'application/json'
}
})
// 检查响应状态
if (!serpRes.ok) {
const errorText = await serpRes.text()
console.error('API error response:', errorText)
throw new Error(`HTTP error! status: ${serpRes.status}, response: ${errorText}`)
}
// 获取响应文本并解析 JSON
const serpData = await serpRes.json()
console.log('API response:', JSON.stringify(serpData, null, 2))
// 验证响应格式
if (!serpData || serpData.code !== 200 || !Array.isArray(serpData.data)) {
console.error('Invalid response format:', serpData)
throw new Error('Invalid response format from server')
} }
})
serpRes = await serpRes.json()
let res = serpRes.data // 确保返回的数据是数组
return `images search results in json format:\n${JSON.stringify(res)}. the murl field is actual picture url. You should use sendPicture to send them` const res = Array.isArray(serpData.data) ? serpData.data : []
if (res.length === 0) {
return {
error: 'No images found',
data: []
}
}
// 验证每个图片 URL
const validImages = res.filter(img => {
if (!img || typeof img !== 'object') return false
if (!img.murl || typeof img.murl !== 'string') return false
if (!img.murl.startsWith('http')) return false
return true
})
if (validImages.length === 0) {
return {
error: 'No valid image URLs found',
data: []
}
}
return `images search results in json format:\n${JSON.stringify(validImages)}. the murl field is actual picture url. You should use sendPicture to send them`
} catch (error) {
console.error('Search image error:', error)
return {
error: `Failed to search images: ${error.message}`,
data: []
}
}
} }
description = 'Useful when you want to search images from the Internet.' description = 'Useful when you want to search images from the Internet.'

View file

@ -43,4 +43,4 @@ export class SendAvatarTool extends AbstractTool {
} }
description = 'Useful when you want to send the user avatar to the group. Note that if you want to process user\'s avatar, it is advisable to utilize the ProcessPictureTool and input the qq of target user. If no extra description needed, just reply <EMPTY> at the next turn' description = 'Useful when you want to send the user avatar to the group. Note that if you want to process user\'s avatar, it is advisable to utilize the ProcessPictureTool and input the qq of target user. If no extra description needed, just reply <EMPTY> at the next turn'
} }

View file

@ -58,4 +58,4 @@ export class SendMessageToSpecificGroupOrUserTool extends AbstractTool {
} }
description = 'Useful when you want to send a text message to specific user or group. If no extra description needed, just reply <EMPTY> at the next turn' description = 'Useful when you want to send a text message to specific user or group. If no extra description needed, just reply <EMPTY> at the next turn'
} }