diff --git a/packages/engine-chronocat-api/src/api/internal/assets/get.ts b/packages/engine-chronocat-api/src/api/internal/assets/get.ts index c35c8a4..032b174 100644 --- a/packages/engine-chronocat-api/src/api/internal/assets/get.ts +++ b/packages/engine-chronocat-api/src/api/internal/assets/get.ts @@ -1,31 +1,63 @@ -import type { Media } from '@chronocat/red' +import type { AssetRequest } from '@chronocat/red' import { ChatType } from '@chronocat/red' import type { ChronocatContext } from '@chronocat/shell' -import { downloadRichMedia } from '../../../definitions/msgService' -import { richMediaDownloadMap } from '../../../globalVars' +import { + downloadRichMedia, + fetchMarketEmoticonAioImage, +} from '../../../definitions/msgService' +import { emojiDownloadMap, richMediaDownloadMap } from '../../../globalVars' export const buildAssetsGet = (ctx: ChronocatContext) => async (raw: string) => { const data = JSON.parse( Buffer.from(raw, 'base64url').toString('utf-8'), - ) as Media + ) as AssetRequest - const downloadId = data.msgId + '::' + data.elementId + if ('type' in data) { + switch (data.type) { + case 'marketface': { + const downloadId = data.tabId + '::' + data.faceId - const downloadCompletePromise = new Promise((res, rej) => { - richMediaDownloadMap[downloadId] = res - void ctx.chronocat.sleep(1000).then(rej) - }) + const downloadCompletePromise = new Promise((res, rej) => { + emojiDownloadMap[downloadId] = res + void ctx.chronocat.sleep(5000).then(rej) + }) - if (data.chatType === ChatType.Private && !data.peerUid.startsWith('u_')) - data.peerUid = ctx.chronocat.uix.getUid(data.peerUid)! + await fetchMarketEmoticonAioImage({ + marketEmoticonAioImageReq: { + eId: data.faceId, + epId: data.tabId, + name: data.name, + width: 200, + height: 200, + jobType: 0, + encryptKey: data.key, + filePath: data.filePath, + downloadType: 3, + }, + }) - await downloadRichMedia({ - getReq: { - ...data, - downloadType: 1, - }, - }) + return await downloadCompletePromise + } + } + } else { + const downloadId = data.msgId + '::' + data.elementId - return await downloadCompletePromise + const downloadCompletePromise = new Promise((res, rej) => { + richMediaDownloadMap[downloadId] = res + void ctx.chronocat.sleep(1000).then(rej) + }) + + if (data.chatType === ChatType.Private && !data.peerUid.startsWith('u_')) + data.peerUid = ctx.chronocat.uix.getUid(data.peerUid)! + + await downloadRichMedia({ + getReq: { + ...data, + downloadType: 1, + }, + }) + + return await downloadCompletePromise + } } diff --git a/packages/engine-chronocat-api/src/api/message/create/messager.ts b/packages/engine-chronocat-api/src/api/message/create/messager.ts index 5c2337f..406db15 100644 --- a/packages/engine-chronocat-api/src/api/message/create/messager.ts +++ b/packages/engine-chronocat-api/src/api/message/create/messager.ts @@ -277,6 +277,19 @@ export class Messager { return } + case `${this.ctx.chronocat.platform}:marketface`: { + this.children.push( + r.marketFace( + Number(attrs['tabId']), + attrs['faceId'] as string, + attrs['key'] as string, + ), + ) + + this.isEndLine = false + return + } + case 'quote': { const [author] = this.ctx.chronocat.h.select(children, 'author') diff --git a/packages/engine-chronocat-api/src/api/message/create/r.ts b/packages/engine-chronocat-api/src/api/message/create/r.ts index 852b119..7580669 100644 --- a/packages/engine-chronocat-api/src/api/message/create/r.ts +++ b/packages/engine-chronocat-api/src/api/message/create/r.ts @@ -114,6 +114,28 @@ const b = () => { }, }), + marketFace: ( + tabId: number, + faceId: string, + key: string, + ): O.Partial => ({ + elementType: 11, + elementId: '', + marketFaceElement: { + itemType: 6, + faceInfo: 1, + emojiPackageId: tabId, + subType: 3, + mediaType: 0, + imageWidth: 200, + imageHeight: 200, + faceName: '[动画表情]', + emojiId: faceId, + key: key, + emojiType: 1, + }, + }), + pcPoke: (pokeType: number): O.Partial => ({ elementId: '0', elementType: 6, diff --git a/packages/engine-chronocat-api/src/definitions/msgService.ts b/packages/engine-chronocat-api/src/definitions/msgService.ts index 2eac4a8..9981cca 100644 --- a/packages/engine-chronocat-api/src/definitions/msgService.ts +++ b/packages/engine-chronocat-api/src/definitions/msgService.ts @@ -151,3 +151,22 @@ export const deleteActiveChatByUid = define, [string]>( 'ns-ntApi-2', 'nodeIKernelMsgService/deleteActiveChatByUid', ) + +export const fetchMarketEmoticonAioImage = define< + never, + [ + { + marketEmoticonAioImageReq: { + eId: string // '94c8ffa6977fd17e8b180b312cdddc28' + epId: number // 235125 + name: string // '[抱抱]' + width: 200 + height: 200 + jobType: 0 + encryptKey: string // 'ea4dc6c26b6f9c31' + filePath: string + downloadType: 3 | 4 + } + }, + ] +>('ns-ntApi-2', 'nodeIKernelMsgService/fetchMarketEmoticonAioImage') diff --git a/packages/engine-chronocat-api/src/globalVars.ts b/packages/engine-chronocat-api/src/globalVars.ts index a277567..ce8bc66 100644 --- a/packages/engine-chronocat-api/src/globalVars.ts +++ b/packages/engine-chronocat-api/src/globalVars.ts @@ -11,6 +11,7 @@ export const groupMap: Record = {} export const roleMap: Record> = {} export const friendMap: Record = {} export const richMediaDownloadMap: Record void> = {} +export const emojiDownloadMap: Record void> = {} export const sendQueue: ((msg: RedMessage) => void)[] = [] export const sendCallbackMap: Record void> = {} diff --git a/packages/engine-chronocat-api/src/handler.ts b/packages/engine-chronocat-api/src/handler.ts index eb003ce..88ce9b9 100644 --- a/packages/engine-chronocat-api/src/handler.ts +++ b/packages/engine-chronocat-api/src/handler.ts @@ -3,6 +3,7 @@ import type { MsgsIncludeSelf, OnAddSendMsg, OnBuddyListChange, + OnEmojiDownloadComplete, OnGroupListUpdate, OnMemberInfoChange, OnMemberListChange, @@ -20,6 +21,7 @@ import type { ChronocatContext } from '@chronocat/shell' import type { IpcManData } from 'ipcman' import { chronoEventEmitter, + emojiDownloadMap, friendMap, groupMap, requestCallbackMap, @@ -131,6 +133,19 @@ const responseDispatcher = async ( return } + case 'nodeIKernelMsgListener/onEmojiDownloadComplete': { + const { notifyInfo } = payload as OnEmojiDownloadComplete + + const downloadId = `${notifyInfo.emojiPackageId}::${notifyInfo.emojiId}` + + if (notifyInfo.downloadType === 0 && emojiDownloadMap[downloadId]) { + emojiDownloadMap[downloadId]!(notifyInfo.path) + delete emojiDownloadMap[downloadId] + } + + return + } + case 'nodeIKernelProfileListener/onProfileSimpleChanged': case 'nodeIKernelProfileListener/onProfileDetailInfoChanged': case 'nodeIKernelGroupListener/onSearchMemberChange': diff --git a/packages/engine-chronocat-event/src/parser/index.ts b/packages/engine-chronocat-event/src/parser/index.ts index 4b31f30..2a9839d 100644 --- a/packages/engine-chronocat-event/src/parser/index.ts +++ b/packages/engine-chronocat-event/src/parser/index.ts @@ -552,6 +552,34 @@ async function parseElements( break } + case 11: { + elements.push( + ctx.chronocat.h( + `${ctx.chronocat.platform}:marketface`, + { + tabId: m.marketFaceElement!.emojiPackageId, + faceId: m.marketFaceElement!.emojiId, + key: m.marketFaceElement!.key, + }, + [ + ctx.chronocat.h('img', { + src: `${config.self_url}/v1/assets/${Buffer.from( + JSON.stringify({ + type: 'marketface', + tabId: m.marketFaceElement!.emojiPackageId, + faceId: m.marketFaceElement!.emojiId, + key: m.marketFaceElement!.key, + name: m.marketFaceElement!.faceName, + filePath: m.marketFaceElement!.staticFacePath, + }), + ).toString('base64url')}`, + }), + ], + ), + ) + break + } + default: break } diff --git a/packages/red/src/redEntity.ts b/packages/red/src/redEntity.ts index 31aa4ad..dbbeb90 100644 --- a/packages/red/src/redEntity.ts +++ b/packages/red/src/redEntity.ts @@ -275,7 +275,7 @@ export interface Element { inlineKeyboardElement?: InlineKeyboardElement liveGiftElement?: unknown markdownElement?: MarkdownElement - marketFaceElement?: unknown + marketFaceElement?: MarketFaceElement multiForwardMsgElement?: unknown pttElement?: PttElement replyElement?: ReplyElement @@ -653,6 +653,17 @@ export interface Media { downloadType: number } +export interface MarketFaceAssetRequest { + type: 'marketface' + tabId: number + faceId: string + key: string + name: string + filePath: string +} + +export type AssetRequest = MarketFaceAssetRequest | Media + export interface InlineKeyboardElement { rows: InlineKeyboardRow[] } @@ -762,6 +773,41 @@ export interface MarkdownElement { content: string } +export interface MarketFaceElement { + itemType: 6 + faceInfo: 1 + emojiPackageId: number // 235125 + subType: 3 + mediaType: 0 + imageWidth: number // 200 + imageHeight: number // 200 + faceName: string // '[好耶]' + emojiId: string // 'e6e7817c449efdea0be5ceeef3a40c06' + key: string // 'ea4dc6c26b6f9c31' + param: unknown + mobileParam: unknown + sourceType: 0 + startTime: 0 + endTime: 0 + emojiType: 1 + hasIpProduct: 0 + voiceItemHeightArr: unknown + sourceName: unknown + sourceJumpUrl: unknown + sourceTypeName: string // '' + backColor: unknown + volumeColor: unknown + staticFacePath: string + dynamicFacePath: string + supportSize: MarketFaceElementSupportSize[] + apngSupportSize: unknown +} + +export interface MarketFaceElementSupportSize { + width: number // 300 + height: number // 300 +} + export interface ArkElement { bytesData: string linkInfo: never diff --git a/packages/red/src/redIpcEntity.ts b/packages/red/src/redIpcEntity.ts index 2f26c26..f077086 100644 --- a/packages/red/src/redIpcEntity.ts +++ b/packages/red/src/redIpcEntity.ts @@ -75,6 +75,27 @@ export interface OnRichMediaDownloadComplete { } } +export interface OnEmojiDownloadComplete { + notifyInfo: { + result: 0 + errMsg: string // '' + emojiType: 0 + md5: string // '' + resId: string // '' + path: string + extraData: Record + emojiId: string // '94c8ffa6977fd17e8b180b312cdddc28' + emojiPackageId: string // '235125' + + /** + * 下载富媒体的类型。发送时 3 为 PNG,4 为动图;接收时 0 为 PNG,4 为动图。 + */ + downloadType: 0 | 4 + + dynamicFacePath: string // '' + } +} + export interface OnGroupListUpdate { updateType: 1 groupList: Group[]