diff --git a/electron.vite.config.ts b/electron.vite.config.ts index ae6ddfd..deb8dc0 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -33,6 +33,7 @@ let config = { ...external.map(genCpModule), {src: './manifest.json', dest: 'dist'}, {src: './icon.jpg', dest: 'dist'}, {src: './src/ntqqapi/external/crychic/crychic-win32-x64.node', dest: 'dist/main/'}, + {src: './src/ntqqapi/external/moehook/MoeHook-win32-x64.node', dest: 'dist/main/', rename: 'MoeHook.node'}, ] })] }, diff --git a/package-lock.json b/package-lock.json index 4c1db1c..44996a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "compressing": "^1.10.0", "express": "^4.18.2", + "fast-xml-parser": "^4.3.6", "file-type": "^19.0.0", "fluent-ffmpeg": "^2.1.2", "level": "^8.0.1", @@ -3720,6 +3721,27 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fast-xml-parser": { + "version": "4.3.6", + "resolved": "https://mirrors.cloud.tencent.com/npm/fast-xml-parser/-/fast-xml-parser-4.3.6.tgz", + "integrity": "sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -6052,6 +6074,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://mirrors.cloud.tencent.com/npm/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" + }, "node_modules/strtok3": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", diff --git a/package.json b/package.json index ac1d66f..1e93e0b 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "dependencies": { "compressing": "^1.10.0", "express": "^4.18.2", + "fast-xml-parser": "^4.3.6", "file-type": "^19.0.0", "fluent-ffmpeg": "^2.1.2", "level": "^8.0.1", diff --git a/src/onebot11/action/file/GetFile.ts b/src/onebot11/action/file/GetFile.ts index f9045ee..fc0ced2 100644 --- a/src/onebot11/action/file/GetFile.ts +++ b/src/onebot11/action/file/GetFile.ts @@ -6,6 +6,7 @@ import {log, sleep, uri2local} from "../../../common/utils"; import {NTQQFileApi} from "../../../ntqqapi/api/file"; import {ActionName} from "../types"; import {FileElement, RawMessage, VideoElement} from "../../../ntqqapi/types"; +import {FileCache} from "../../../common/types"; export interface GetFilePayload { file: string // 文件名或者fileUuid @@ -29,6 +30,25 @@ export class GetFileBase extends BaseAction { } return {id: element.elementId, element: element.fileElement} } + private async download(cache: FileCache, file: string){ + log("需要调用 NTQQ 下载文件api") + if (cache.msgId) { + let msg = await dbUtil.getMsgByLongId(cache.msgId) + if (msg){ + log("找到了文件 msg", msg) + let element = this.getElement(msg); + log("找到了文件 element", element); + // 构建下载函数 + await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, + element.id, "", "", true) + await sleep(1000); + msg = await dbUtil.getMsgByLongId(cache.msgId) + log("下载完成后的msg", msg) + cache.filePath = this.getElement(msg).element.filePath + dbUtil.addFileCache(file, cache).then() + } + } + } protected async _handle(payload: GetFilePayload): Promise { const cache = await dbUtil.getFileCache(payload.file) const {autoDeleteFile, enableLocalFile2Url, autoDeleteFileSecond} = getConfigUtil().getConfig() @@ -41,35 +61,19 @@ export class GetFileBase extends BaseAction { try { await fs.access(cache.filePath, fs.constants.F_OK) } catch (e) { - log("file not found", e) + // log("file not found", e) if (cache.url){ const downloadResult = await uri2local(cache.url) if (downloadResult.success) { cache.filePath = downloadResult.path dbUtil.addFileCache(payload.file, cache).then() } else { - throw new Error("file download failed. " + downloadResult.errMsg) + await this.download(cache, payload.file) } } else{ // 没有url的可能是私聊文件或者群文件,需要自己下载 - log("需要调用 NTQQ 下载文件api") - if (cache.msgId) { - let msg = await dbUtil.getMsgByLongId(cache.msgId) - if (msg){ - log("找到了文件 msg", msg) - let element = this.getElement(msg); - log("找到了文件 element", element); - // 构建下载函数 - await NTQQFileApi.downloadMedia(msg.msgId, msg.chatType, msg.peerUid, - element.id, "", "", true) - await sleep(1000); - msg = await dbUtil.getMsgByLongId(cache.msgId) - log("下载完成后的msg", msg) - cache.filePath = this.getElement(msg).element.filePath - dbUtil.addFileCache(payload.file, cache).then() - } - } + await this.download(cache, payload.file) } } diff --git a/src/onebot11/action/index.ts b/src/onebot11/action/index.ts index dce56d1..76ef78d 100644 --- a/src/onebot11/action/index.ts +++ b/src/onebot11/action/index.ts @@ -36,7 +36,7 @@ import GetImage from "./file/GetImage"; import GetRecord from "./file/GetRecord"; import GoCQHTTPMarkMsgAsRead from "./msg/MarkMsgAsRead"; import CleanCache from "./system/CleanCache"; -import {GoCQHTTPUploadGroupFile, GoCQHTTPUploadPrivateFile} from "./go-cqhttp/UploadGroupFile"; +import {GoCQHTTPUploadGroupFile, GoCQHTTPUploadPrivateFile} from "./go-cqhttp/UploadFile"; import {GetConfigAction, SetConfigAction} from "./llonebot/Config"; import GetGroupAddRequest from "./llonebot/GetGroupAddRequest"; import SetQQAvatar from './llonebot/SetQQAvatar' diff --git a/src/onebot11/constructor.ts b/src/onebot11/constructor.ts index 89310a7..7425a23 100644 --- a/src/onebot11/constructor.ts +++ b/src/onebot11/constructor.ts @@ -1,3 +1,4 @@ +import fastXmlParser, {XMLParser} from 'fast-xml-parser'; import { OB11Group, OB11GroupMember, @@ -40,6 +41,7 @@ import {OB11GroupTitleEvent} from "./event/notice/OB11GroupTitleEvent"; import {OB11GroupCardEvent} from "./event/notice/OB11GroupCardEvent"; import {OB11GroupDecreaseEvent} from "./event/notice/OB11GroupDecreaseEvent"; import {NTQQGroupApi} from "../ntqqapi/api"; +import {OB11GroupMsgEmojiLikeEvent} from "./event/notice/OB11MsgEmojiLikeEvent"; let lastRKeyUpdateTime = 0; @@ -140,35 +142,9 @@ export class OB11Constructor { // message_data["data"]["file"] = element.picElement.sourcePath message_data["data"]["file"] = element.picElement.fileName // message_data["data"]["path"] = element.picElement.sourcePath - const url = element.picElement.originImageUrl - const fileMd5 = element.picElement.md5HexStr - const fileUuid = element.picElement.fileUuid - // let currentRKey = config.imageRKey || "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64" - let currentRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64" - if (url) { - if (url.startsWith("/download")) { - if (url.includes("&rkey=")) { - // 正则提取rkey - // const rkey = url.match(/&rkey=([^&]+)/)[1] - // // log("图片url已有rkey", rkey) - // if (rkey != currentRKey){ - // config.imageRKey = rkey - // if (Date.now() - lastRKeyUpdateTime > 1000 * 60) { - // lastRKeyUpdateTime = Date.now() - // getConfigUtil().setConfig(config) - // } - // } - message_data["data"]["url"] = IMAGE_HTTP_HOST + url - } else { - // 有可能会碰到appid为1406的,这个不能使用新的NT域名,并且需要把appid改为1407才可访问 - message_data["data"]["url"] = `${IMAGE_HTTP_HOST}/download?appid=1407&fileid=${fileUuid}&rkey=${currentRKey}&spec=0` - } - } else { - message_data["data"]["url"] = IMAGE_HTTP_HOST + url - } - } else if (fileMd5) { - message_data["data"]["url"] = `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${fileMd5.toUpperCase()}/0` - } + // let currentRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64" + + message_data["data"]["url"] = await NTQQFileApi.getImageUrl(msg); // message_data["data"]["file_id"] = element.picElement.fileUuid message_data["data"]["file_size"] = element.picElement.fileSize dbUtil.addFileCache(element.picElement.fileName, { @@ -241,6 +217,13 @@ export class OB11Constructor { } else if (element.marketFaceElement) { message_data["type"] = OB11MessageDataType.mface; message_data["data"]["text"] = element.marketFaceElement.faceName; + const md5 = element.marketFaceElement.emojiId; + // 取md5的前两位 + const dir = md5.substring(0, 2); + // 获取组装url + // const url = `https://p.qpic.cn/CDN_STATIC/0/data/imgcache/htdocs/club/item/parcel/item/${dir}/${md5}/300x300.gif?max_age=31536000` + const url = `https://gxh.vip.qq.com/club/item/parcel/item/${dir}/${md5}/raw300.gif`; + message_data["data"]["url"] = url; } else if (element.markdownElement) { message_data["type"] = OB11MessageDataType.markdown; message_data["data"]["data"] = element.markdownElement.content; @@ -338,9 +321,42 @@ export class OB11Constructor { } if (grayTipElement) { - if (grayTipElement.subElementType == GrayTipElementSubType.INVITE_NEW_MEMBER) { + const xmlElement = grayTipElement.xmlElement + + if (xmlElement?.templId === "10382") { + // 表情回应消息 + // "content": + // " + // + // + // + // + // ", + const emojiLikeData = new fastXmlParser.XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: "" + }).parse(xmlElement.content) + log("收到表情回应我的消息", emojiLikeData) + try { + const senderUin = emojiLikeData.gtip.qq.jp; + const msgSeq = emojiLikeData.gtip.url.msgseq; + const emojiId = emojiLikeData.gtip.face.id; + const msg = await dbUtil.getMsgBySeqId(msgSeq); + if (!msg) { + return; + } + return new OB11GroupMsgEmojiLikeEvent(parseInt(msg.peerUid), parseInt(senderUin), msg.msgShortId, [{ + emoji_id: emojiId, + count: 1 + }]); + } catch (e) { + log("解析表情回应消息失败", e.stack); + } + } + + if (grayTipElement.subElementType == GrayTipElementSubType.INVITE_NEW_MEMBER + && xmlElement?.templId == "10179") { log("收到新人被邀请进群消息", grayTipElement) - const xmlElement = grayTipElement.xmlElement if (xmlElement?.content) { const regex = /jp="(\d+)"/g;