diff --git a/src/laana-v0.1.2/index.ts b/src/laana-v0.1.2/index.ts index d4e500a8..0665acab 100644 --- a/src/laana-v0.1.2/index.ts +++ b/src/laana-v0.1.2/index.ts @@ -1,7 +1,14 @@ import { InstanceContext, NapCatCore } from '@/core'; import { NapCatPathWrapper } from '@/common/path'; +import { LaanaFileUtils } from '@/laana-v0.1.2/utils/file'; +import { LaanaMessageUtils } from '@/laana-v0.1.2/utils/message'; export class NapCatLaanaAdapter { + utils = { + msg: new LaanaMessageUtils(this.core, this), + file: new LaanaFileUtils(this.core, this), + }; + constructor( public core: NapCatCore, public context: InstanceContext, diff --git a/src/laana-v0.1.2/utils/file.ts b/src/laana-v0.1.2/utils/file.ts new file mode 100644 index 00000000..e896b04d --- /dev/null +++ b/src/laana-v0.1.2/utils/file.ts @@ -0,0 +1,51 @@ +import { NapCatCore } from '@/core'; +import { NapCatLaanaAdapter } from '..'; +import { File as LaanaFile } from '@/laana-v0.1.2/types/entity/file'; +import path from 'path'; +import fs from 'fs'; +import fsPromises from 'fs/promises'; +import { httpDownload } from '@/common/file'; +import { randomUUID } from 'crypto'; + +export class LaanaFileUtils { + cacheDir = path.join(this.laana.pathWrapper.cachePath, 'laana'); + + constructor( + public core: NapCatCore, + public laana: NapCatLaanaAdapter, + ) { + } + + async resolveCacheIdFromLaanaFile(laanaFile: LaanaFile) { + if (laanaFile.uri.oneofKind === 'cacheId') { + const cacheFilePath = path.join(this.cacheDir, laanaFile.uri.cacheId); + if (!fs.existsSync(cacheFilePath)) { + throw `请求的缓存不存在: ${laanaFile.uri.cacheId}`; + } + return laanaFile.uri.cacheId; + } else if (laanaFile.uri.oneofKind === 'url') { + return this.createCacheFromUrl(laanaFile.uri.url); + } else if (laanaFile.uri.oneofKind === 'raw') { + return this.createCacheFromBytes(laanaFile.uri.raw); + } else { + throw '不支持的缓存类型'; + } + } + + toLocalPath(cacheId: string) { + return path.join(this.cacheDir, cacheId); + } + + async createCacheFromBytes(bytes: Uint8Array) { + let cacheId = randomUUID(); + while (fs.existsSync(cacheId)) { + cacheId = randomUUID(); + } + await fsPromises.writeFile(path.join(this.cacheDir, cacheId), bytes); + return cacheId; + } + + async createCacheFromUrl(url: string) { + return this.createCacheFromBytes(await httpDownload({ url })); + } +} diff --git a/src/laana-v0.1.2/utils/message.ts b/src/laana-v0.1.2/utils/message.ts index 80d31ffe..a3367904 100644 --- a/src/laana-v0.1.2/utils/message.ts +++ b/src/laana-v0.1.2/utils/message.ts @@ -13,7 +13,7 @@ type Laana2RawConverters = { params: SendMessagePing, ) => PromiseLike<{ elements: SendMessageElement[], - fileCacheId?: string[], + fileCacheIds: string[], }> } @@ -40,7 +40,8 @@ export class LaanaMessageUtils { }; } - const sendElements: SendMessageElement[] = []; + const elements: SendMessageElement[] = []; + const fileCacheIds: string[] = []; if (msgContent.repliedMsgId) { const { msgSeq, msgId, senderUin } = ( @@ -49,7 +50,7 @@ export class LaanaMessageUtils { [msgContent.repliedMsgId] ) ).msgList[0]; - sendElements.push({ + elements.push({ elementType: ElementType.REPLY, elementId: '', replyElement: { @@ -64,7 +65,7 @@ export class LaanaMessageUtils { for (const seg of msgContent.segments) { const content = seg.content; if (content.oneofKind === 'text') { - sendElements.push({ + elements.push({ elementType: ElementType.TEXT, elementId: '', textElement: { @@ -81,7 +82,7 @@ export class LaanaMessageUtils { } if (content.at.uin === '0') { - sendElements.push(at( + elements.push(at( '0', '0', AtType.atAll, '所有人', @@ -91,7 +92,7 @@ export class LaanaMessageUtils { const atMember = await this.core.apis.GroupApi .getGroupMember(params.targetPeer.uin, content.at.uin); if (atMember) { - sendElements.push(at( + elements.push(at( content.at.uin, atMember.uid, AtType.atUser, @@ -103,7 +104,7 @@ export class LaanaMessageUtils { throw '查询用户 UID 失败'; } const info = await this.core.apis.UserApi.getUserDetailInfo(uid); - sendElements.push(at( + elements.push(at( content.at.uin, uid, AtType.atUser, @@ -122,7 +123,7 @@ export class LaanaMessageUtils { const faceType = parsedFaceId >= 222 ? face.AniStickerType ? 3 : 2 : 1; - sendElements.push({ + elements.push({ elementType: ElementType.FACE, elementId: '', faceElement: { @@ -136,14 +137,17 @@ export class LaanaMessageUtils { }, }); } else if (content.oneofKind === 'image') { - // TODO: handle file-like messages - throw 'Unimplemented'; + const cacheId = await this.laana.utils.file.resolveCacheIdFromLaanaFile(content.image); + elements.push(await this.core.apis.FileApi.createValidSendPicElement( + this.laana.utils.file.toLocalPath(cacheId) + )); + fileCacheIds.push(cacheId); } else { throw '未知的消息内容类型'; } } - return { elements: sendElements }; + return { elements, fileCacheIds }; }, file: () => { throw 'Unimplemented'; }, @@ -160,6 +164,7 @@ export class LaanaMessageUtils { faceName: msgContent.displayText ?? '[商城表情]', }, }], + fileCacheIds: [], }), video: () => { throw 'Unimplemented'; },