diff --git a/src/ntqqapi/api/file.ts b/src/ntqqapi/api/file.ts index 19bc1b0..dec2d5c 100644 --- a/src/ntqqapi/api/file.ts +++ b/src/ntqqapi/api/file.ts @@ -26,7 +26,7 @@ export class NTQQFileApi { msgId, elementId, 0, - { downSourceType: 1, triggerType: 1 })).urlResult?.domainUrl[0]?.url; + { downSourceType: 1, triggerType: 1 }))?.urlResult?.domainUrl[0]?.url! } static async getFileType(filePath: string) { diff --git a/src/ntqqapi/constructor.ts b/src/ntqqapi/constructor.ts index 4f1f5f6..31987e8 100644 --- a/src/ntqqapi/constructor.ts +++ b/src/ntqqapi/constructor.ts @@ -77,7 +77,7 @@ export class SendMsgElementConstructor { throw '文件异常,大小为0' } const maxMB = 30; - if (fileSize > 1024 * 1024 * 30){ + if (fileSize > 1024 * 1024 * 30) { throw `图片过大,最大支持${maxMB}MB,当前文件大小${fileSize}B` } const imageSize = await NTQQFileApi.getImageSize(picPath) @@ -104,21 +104,21 @@ export class SendMsgElementConstructor { } } - static async file(filePath: string, fileName: string = ''): Promise { - const { md5, fileName: _fileName, path, fileSize } = await NTQQFileApi.uploadFile(filePath, ElementType.FILE) + static async file(filePath: string, fileName: string = '', folderId: string = ''): Promise { + const { fileName: _fileName, path, fileSize } = await NTQQFileApi.uploadFile(filePath, ElementType.FILE) if (fileSize === 0) { - throw '文件异常,大小为0' + throw '文件异常,大小为 0' } - let element: SendFileElement = { + const element: SendFileElement = { elementType: ElementType.FILE, elementId: '', fileElement: { fileName: fileName || _fileName, - filePath: path, + folderId: folderId, + filePath: path!, fileSize: fileSize.toString(), }, } - return element } @@ -175,7 +175,7 @@ export class SendMsgElementConstructor { setTimeout(useDefaultThumb, 5000) ffmpeg(filePath) - .on('end', () => {}) + .on('end', () => { }) .on('error', (err) => { if (diyThumbPath) { fs.copyFile(diyThumbPath, thumbPath) @@ -280,10 +280,10 @@ export class SendMsgElementConstructor { faceId = parseInt(faceId.toString()) // let faceType = parseInt(faceId.toString().substring(0, 1)); let faceType = 1 - if (faceId >= 222){ + if (faceId >= 222) { faceType = 2 } - if (face?.AniStickerType){ + if (face?.AniStickerType) { faceType = 3; } return { diff --git a/src/ntqqapi/native/cpmodule.ts b/src/ntqqapi/native/cpmodule.ts deleted file mode 100644 index ab98263..0000000 --- a/src/ntqqapi/native/cpmodule.ts +++ /dev/null @@ -1,19 +0,0 @@ -import * as os from "os"; -import path from "node:path"; -import fs from "fs"; - -export function getModuleWithArchName(moduleName: string) { - const systemPlatform = os.platform() - const cpuArch = os.arch() - return `${moduleName}-${systemPlatform}-${cpuArch}.node` -} - -export function cpModule(moduleName: string) { - const currentDir = path.resolve(__dirname); - const fileName = `./${getModuleWithArchName(moduleName)}` - try { - fs.copyFileSync(path.join(currentDir, fileName), path.join(currentDir, `${moduleName}.node`)); - } catch (e) { - - } -} \ No newline at end of file diff --git a/src/ntqqapi/native/crychic/crychic-win32-x64.node b/src/ntqqapi/native/crychic/crychic-win32-x64.node deleted file mode 100644 index 2f3e843..0000000 Binary files a/src/ntqqapi/native/crychic/crychic-win32-x64.node and /dev/null differ diff --git a/src/ntqqapi/native/crychic/index.ts b/src/ntqqapi/native/crychic/index.ts deleted file mode 100644 index a5ffa25..0000000 --- a/src/ntqqapi/native/crychic/index.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { log } from '../../../common/utils' -import { NTQQApi } from '../../ntcall' -import { cpModule } from '../cpmodule' - -type PokeHandler = (id: string, isGroup: boolean) => void -type CrychicHandler = (event: string, id: string, isGroup: boolean) => void - -let pokeRecords: Record = {} - -class Crychic { - private crychic: any = undefined - - loadNode() { - if (!this.crychic) { - try { - cpModule('crychic') - this.crychic = require('./crychic.node') - this.crychic.init() - } catch (e) { - log('crychic加载失败', e) - } - } - } - - registerPokeHandler(fn: PokeHandler) { - this.registerHandler((event, id, isGroup) => { - if (event === 'poke') { - let existTime = pokeRecords[id] - if (existTime) { - if (Date.now() - existTime < 1500) { - return - } - } - pokeRecords[id] = Date.now() - fn(id, isGroup) - } - }) - } - - registerHandler(fn: CrychicHandler) { - if (!this.crychic) return - this.crychic.setCryHandler(fn) - } - - sendFriendPoke(friendUid: string) { - if (!this.crychic) return - this.crychic.sendFriendPoke(parseInt(friendUid)) - NTQQApi.fetchUnitedCommendConfig().then() - } - - sendGroupPoke(groupCode: string, memberUin: string) { - if (!this.crychic) return - this.crychic.sendGroupPoke(parseInt(memberUin), parseInt(groupCode)) - NTQQApi.fetchUnitedCommendConfig().then() - } -} - -export const crychic = new Crychic() \ No newline at end of file diff --git a/src/ntqqapi/native/moehook/hook.ts b/src/ntqqapi/native/moehook/hook.ts deleted file mode 100644 index 05392a6..0000000 --- a/src/ntqqapi/native/moehook/hook.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {cpModule} from "../cpmodule"; -import { qqPkgInfo } from '@/common/utils/QQBasicInfo' - -interface MoeHook { - GetRkey: () => string, // Return '&rkey=xxx' - HookRkey: (version: string) => string -} - - -class HookApi { - private readonly moeHook: MoeHook | null = null; - - constructor() { - cpModule('MoeHoo'); - try { - this.moeHook = require('./MoeHoo.node'); - console.log("hook rkey qq version", this.moeHook!.HookRkey(qqPkgInfo.version)); - console.log("hook rkey地址", this.moeHook!.HookRkey(qqPkgInfo.version)); - } catch (e) { - console.log('加载 moehoo 失败', e); - } - } - - getRKey(): string { - return this.moeHook?.GetRkey() || ''; - } - - isAvailable() { - return !!this.moeHook; - } -} - -// export const hookApi = new HookApi(); diff --git a/src/ntqqapi/services/NodeIKernelRichMediaService.ts b/src/ntqqapi/services/NodeIKernelRichMediaService.ts new file mode 100644 index 0000000..42dbc72 --- /dev/null +++ b/src/ntqqapi/services/NodeIKernelRichMediaService.ts @@ -0,0 +1,270 @@ +import { GetFileListParam, MessageElement, Peer } from '../types' +import { GeneralCallResult } from './common' + +export enum UrlFileDownloadType { + KUNKNOWN, + KURLFILEDOWNLOADPRIVILEGEICON, + KURLFILEDOWNLOADPHOTOWALL, + KURLFILEDOWNLOADQZONE, + KURLFILEDOWNLOADCOMMON, + KURLFILEDOWNLOADINSTALLAPP +} + +export enum RMBizTypeEnum { + KUNKNOWN, + KC2CFILE, + KGROUPFILE, + KC2CPIC, + KGROUPPIC, + KDISCPIC, + KC2CVIDEO, + KGROUPVIDEO, + KC2CPTT, + KGROUPPTT, + KFEEDCOMMENTPIC, + KGUILDFILE, + KGUILDPIC, + KGUILDPTT, + KGUILDVIDEO +} + +export interface CommonFileInfo { + bizType: number + chatType: number + elemId: string + favId: string + fileModelId: string + fileName: string + fileSize: string + md5: string + md510m: string + msgId: string + msgTime: string + parent: string + peerUid: string + picThumbPath: Array + sha: string + sha3: string + subId: string + uuid: string +} + +export interface NodeIKernelRichMediaService { + //getVideoPlayUrl(peer, msgId, elemId, videoCodecFormat, VideoRequestWay.KHAND, cb) + // public enum VideoCodecFormatType { + // KCODECFORMATH264, + // KCODECFORMATH265, + // KCODECFORMATH266, + // KCODECFORMATAV1 + // } + // public enum VideoRequestWay { + // KUNKNOW, + // KHAND, + // KAUTO + // } + getVideoPlayUrl(peer: Peer, msgId: string, elemId: string, videoCodecFormat: number, VideoRequestWay: number): Promise + + //exParams (RMReqExParams) + // this.downSourceType = i2 + // this.triggerType = i3 + //peer, msgId, elemId, videoCodecFormat, exParams + // 1 0 频道在用 + // 1 1 + // 0 2 + + // public static final int KCOMMONREDENVELOPEMSGTYPEINMSGBOX = 1007 + // public static final int KDOWNSOURCETYPEAIOINNER = 1 + // public static final int KDOWNSOURCETYPEBIGSCREEN = 2 + // public static final int KDOWNSOURCETYPEHISTORY = 3 + // public static final int KDOWNSOURCETYPEUNKNOWN = 0 + + // public static final int KTRIGGERTYPEAUTO = 1 + // public static final int KTRIGGERTYPEMANUAL = 0 + + getVideoPlayUrlV2(peer: Peer, msgId: string, elemId: string, videoCodecFormat: number, exParams: { downSourceType: number, triggerType: number }): Promise, + videoCodecFormat: number + } + }> + + getRichMediaFileDir(elementType: number, downType: number, isTemp: boolean): unknown + + // this.senderUid = "" + // this.peerUid = "" + // this.guildId = "" + // this.elem = new MsgElement() + // this.downloadType = i2 + // this.thumbSize = i3 + // this.msgId = j2 + // this.msgRandom = j3 + // this.msgSeq = j4 + // this.msgTime = j5 + // this.chatType = i4 + // this.senderUid = str + // this.peerUid = str2 + // this.guildId = str3 + // this.elem = msgElement + // this.useHttps = num + + getVideoPlayUrlInVisit(arg: { + downloadType: number, + thumbSize: number, + msgId: string, + msgRandom: string, + msgSeq: string, + msgTime: string, + chatType: number, + senderUid: string, + peerUid: string, + guildId: string, + ele: MessageElement, + useHttps: boolean + }): Promise + + //arg双端number + isFileExpired(arg: number): unknown + + deleteGroupFolder(GroupCode: string, FolderId: string): Promise + + //参数与getVideoPlayUrlInVisit一样 + downloadRichMediaInVisit(arg: { + downloadType: number, + thumbSize: number, + msgId: string, + msgRandom: string, + msgSeq: string, + msgTime: string, + chatType: number, + senderUid: string, + peerUid: string, + guildId: string, + ele: MessageElement, + useHttps: boolean + }): unknown + //arg3为“” + downloadFileForModelId(peer: Peer, ModelId: string[], arg3: string): unknown + //第三个参数 Array + // this.fileId = "" + // this.fileName = "" + // this.fileId = str + // this.fileName = str2 + // this.fileSize = j2 + // this.fileModelId = j3 + + downloadFileForFileUuid(peer: Peer, uuid: string, arg3: { + fileId: string, + fileName: string, + fileSize: string, + fileModelId: string + }[]): Promise + + downloadFileByUrlList(fileDownloadTyp: UrlFileDownloadType, urlList: Array): unknown + + downloadFileForFileInfo(fileInfo: CommonFileInfo[], savePath: string): unknown + + createGroupFolder(GroupCode: string, FolderName: string): Promise } }> + + downloadFile(commonFile: CommonFileInfo, arg2: unknown, arg3: unknown, savePath: string): unknown + + createGroupFolder(arg1: unknown, arg2: unknown): unknown + + downloadGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown + + renameGroupFolder(arg1: unknown, arg2: unknown, arg3: unknown): unknown + + deleteGroupFolder(arg1: unknown, arg2: unknown): unknown + + deleteTransferInfo(arg1: unknown, arg2: unknown): unknown + + cancelTransferTask(arg1: unknown, arg2: unknown, arg3: unknown): unknown + + cancelUrlDownload(arg: unknown): unknown + + updateOnlineVideoElemStatus(arg: unknown): unknown + + getGroupSpace(arg: unknown): unknown + + getGroupFileList(groupCode: string, params: GetFileListParam): Promise + + getGroupFileInfo(arg1: unknown, arg2: unknown): unknown + + getGroupTransferList(arg1: unknown, arg2: unknown): unknown + + renameGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown + + moveGroupFile(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown + + transGroupFile(arg1: unknown, arg2: unknown): unknown + + searchGroupFile( + keywords: Array, + param: { + groupIds: Array, + fileType: number, + context: string, + count: number, + sortType: number, + groupNames: Array + }): Promise + searchGroupFileByWord(arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown, arg5: unknown): unknown + + deleteGroupFile(GroupCode: string, params: Array, Files: Array): Promise + failFileIdList: Array + } + }> + + translateEnWordToZn(words: string[]): Promise + + getScreenOCR(path: string): Promise + + batchGetGroupFileCount(Gids: Array): Promise, groupFileCounts: Array }> + + queryPicDownloadSize(arg: unknown): unknown + + searchGroupFile(arg1: unknown, arg2: unknown): unknown + + searchMoreGroupFile(arg: unknown): unknown + + cancelSearcheGroupFile(arg1: unknown, arg2: unknown, arg3: unknown): unknown + + onlyDownloadFile(peer: Peer, arg2: unknown, arg3: Array<{ + fileId: string, + fileName: string, + fileSize: string, + fileModelId: string + } + >): unknown + + onlyUploadFile(arg1: unknown, arg2: unknown): unknown + + isExtraLargePic(arg1: unknown, arg2: unknown, arg3: unknown): unknown + + uploadRMFileWithoutMsg(arg: { + bizType: RMBizTypeEnum, + filePath: string, + peerUid: string, + transferId: string + useNTV2: string + }): Promise + + isNull(): boolean +} \ No newline at end of file diff --git a/src/ntqqapi/services/index.ts b/src/ntqqapi/services/index.ts index 88079a8..0fa9633 100644 --- a/src/ntqqapi/services/index.ts +++ b/src/ntqqapi/services/index.ts @@ -4,4 +4,5 @@ export * from './NodeIKernelGroupService' export * from './NodeIKernelProfileLikeService' export * from './NodeIKernelMsgService' export * from './NodeIKernelMSFService' -export * from './NodeIKernelUixConvertService' \ No newline at end of file +export * from './NodeIKernelUixConvertService' +export * from './NodeIKernelRichMediaService' \ No newline at end of file diff --git a/src/ntqqapi/types/msg.ts b/src/ntqqapi/types/msg.ts index 775dd9a..540aa88 100644 --- a/src/ntqqapi/types/msg.ts +++ b/src/ntqqapi/types/msg.ts @@ -1,6 +1,15 @@ import { GroupMemberRole } from './group' +export interface GetFileListParam { + sortType: number + fileCount: number + startIndex: number + sortOrder: number + showOnlinedocFolder: number +} + export enum ElementType { + UNKNOWN = 0, TEXT = 1, PIC = 2, FILE = 3, @@ -8,8 +17,30 @@ export enum ElementType { VIDEO = 5, FACE = 6, REPLY = 7, + WALLET = 9, + GreyTip = 8, //Poke别叫戳一搓了 官方名字拍一拍 戳一戳是另一个名字 ARK = 10, MFACE = 11, + LIVEGIFT = 12, + STRUCTLONGMSG = 13, + MARKDOWN = 14, + GIPHY = 15, + MULTIFORWARD = 16, + INLINEKEYBOARD = 17, + INTEXTGIFT = 18, + CALENDAR = 19, + YOLOGAMERESULT = 20, + AVRECORD = 21, + FEED = 22, + TOFURECORD = 23, + ACEBUBBLE = 24, + ACTIVITY = 25, + TOFU = 26, + FACEBUBBLE = 27, + SHARELOCATION = 28, + TASKTOPMSG = 29, + RECOMMENDEDMSG = 43, + ACTIONBAR = 44 } export interface SendTextElement { @@ -101,18 +132,19 @@ export interface ReplyElement { } export interface FileElement { - fileMd5?: '' + fileMd5?: string fileName: string filePath: string fileSize: string picHeight?: number picWidth?: number - picThumbPath?: {} - file10MMd5?: '' - fileSha?: '' - fileSha3?: '' - fileUuid?: '' - fileSubId?: '' + folderId?: string + picThumbPath?: Map + file10MMd5?: string + fileSha?: string + fileSha3?: string + fileUuid?: string + fileSubId?: string thumbFileSize?: number fileBizId?: number } diff --git a/src/ntqqapi/wrapper.ts b/src/ntqqapi/wrapper.ts index 40bfc8f..726a4e1 100644 --- a/src/ntqqapi/wrapper.ts +++ b/src/ntqqapi/wrapper.ts @@ -5,7 +5,8 @@ import { NodeIKernelProfileLikeService, NodeIKernelMSFService, NodeIKernelMsgService, - NodeIKernelUixConvertService + NodeIKernelUixConvertService, + NodeIKernelRichMediaService } from './services' import os from 'node:os' const Process = require('node:process') @@ -19,6 +20,7 @@ export interface NodeIQQNTWrapperSession { getMsgService(): NodeIKernelMsgService getMSFService(): NodeIKernelMSFService getUixConvertService(): NodeIKernelUixConvertService + getRichMediaService(): NodeIKernelRichMediaService } export interface WrapperApi { diff --git a/src/onebot11/action/go-cqhttp/UploadFile.ts b/src/onebot11/action/go-cqhttp/UploadFile.ts index 2a9cc3a..08a0b69 100644 --- a/src/onebot11/action/go-cqhttp/UploadFile.ts +++ b/src/onebot11/action/go-cqhttp/UploadFile.ts @@ -1,50 +1,72 @@ +import fs from 'node:fs' import BaseAction from '../BaseAction' -import { getGroup, getUidByUin } from '@/common/data' +import { getGroup } from '@/common/data' import { ActionName } from '../types' import { SendMsgElementConstructor } from '@/ntqqapi/constructor' import { ChatType, SendFileElement } from '@/ntqqapi/types' -import fs from 'fs' -import { NTQQMsgApi } from '@/ntqqapi/api/msg' import { uri2local } from '@/common/utils' import { Peer } from '@/ntqqapi/types' +import { sendMsg } from '../msg/SendMsg' +import { NTQQUserApi, NTQQFriendApi } from '@/ntqqapi/api' interface Payload { - user_id: number - group_id?: number + user_id: number | string + group_id?: number | string file: string name: string - folder: string + folder?: string + folder_id?: string } -class GoCQHTTPUploadFileBase extends BaseAction { +export class GoCQHTTPUploadGroupFile extends BaseAction { actionName = ActionName.GoCQHTTP_UploadGroupFile - getPeer(payload: Payload): Peer { - if (payload.user_id) { - return { chatType: ChatType.friend, peerUid: getUidByUin(payload.user_id.toString())! } - } - return { chatType: ChatType.group, peerUid: payload.group_id?.toString()! } - } - protected async _handle(payload: Payload): Promise { + const group = await getGroup(payload.group_id?.toString()!) + if (!group) { + throw new Error(`群组${payload.group_id}不存在`) + } let file = payload.file if (fs.existsSync(file)) { file = `file://${file}` } const downloadResult = await uri2local(file) - if (downloadResult.errMsg) { + if (!downloadResult.success) { throw new Error(downloadResult.errMsg) } - let sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name) - await NTQQMsgApi.sendMsg(this.getPeer(payload), [sendFileEle]) + const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name, payload.folder_id) + await sendMsg({ chatType: ChatType.group, peerUid: group.groupCode }, [sendFileEle], [], true) return null } } -export class GoCQHTTPUploadGroupFile extends GoCQHTTPUploadFileBase { - actionName = ActionName.GoCQHTTP_UploadGroupFile -} - -export class GoCQHTTPUploadPrivateFile extends GoCQHTTPUploadFileBase { +export class GoCQHTTPUploadPrivateFile extends BaseAction { actionName = ActionName.GoCQHTTP_UploadPrivateFile + + async getPeer(payload: Payload): Promise { + if (payload.user_id) { + const peerUid = await NTQQUserApi.getUidByUin(payload.user_id.toString()) + if (!peerUid) { + throw `私聊${payload.user_id}不存在` + } + const isBuddy = await NTQQFriendApi.isBuddy(peerUid) + return { chatType: isBuddy ? ChatType.friend : ChatType.temp, peerUid } + } + throw '缺少参数 user_id' + } + + protected async _handle(payload: Payload): Promise { + const peer = await this.getPeer(payload) + let file = payload.file + if (fs.existsSync(file)) { + file = `file://${file}` + } + const downloadResult = await uri2local(file) + if (!downloadResult.success) { + throw new Error(downloadResult.errMsg) + } + const sendFileEle: SendFileElement = await SendMsgElementConstructor.file(downloadResult.path, payload.name) + await sendMsg(peer, [sendFileEle], [], true) + return null + } }