From e7e06d655fad3da64ad854309e80d0e1ef8cd2a1 Mon Sep 17 00:00:00 2001
From: linyuchen <lin.yu.chen@hotmail.com>
Date: Thu, 25 Apr 2024 23:28:35 +0800
Subject: [PATCH] optimize get file

---
 electron.vite.config.ts             |  1 +
 package-lock.json                   | 27 ++++++++++
 package.json                        |  1 +
 src/onebot11/action/file/GetFile.ts | 42 +++++++++-------
 src/onebot11/action/index.ts        |  2 +-
 src/onebot11/constructor.ts         | 78 +++++++++++++++++------------
 6 files changed, 100 insertions(+), 51 deletions(-)

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<GetFilePayload, GetFileResponse> {
         }
         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<GetFileResponse> {
         const cache = await dbUtil.getFileCache(payload.file)
         const {autoDeleteFile, enableLocalFile2Url, autoDeleteFileSecond} = getConfigUtil().getConfig()
@@ -41,35 +61,19 @@ export class GetFileBase extends BaseAction<GetFilePayload, GetFileResponse> {
         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":
+          //  "<gtip align=\"center\">
+          //    <qq uin=\"u_snYxnEfja-Po_\" col=\"3\" jp=\"3794\"/>
+          //    <nor txt=\"回应了你的\"/>
+          //    <url jp= \"\" msgseq=\"74711\" col=\"3\" txt=\"消息:\"/>
+          //    <face type=\"1\" id=\"76\">  </face>
+          //  </gtip>",
+          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;