Skip to content

Commit

Permalink
feat: packet ocr
Browse files Browse the repository at this point in the history
  • Loading branch information
pk5ls20 committed Jan 12, 2025
1 parent 5778dae commit c8ee371
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 0 deletions.
42 changes: 42 additions & 0 deletions src/core/packet/context/operationContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import { AIVoiceChatType } from "@/core/packet/entities/aiChat";
import { NapProtoDecodeStructType, NapProtoEncodeStructType } from "@napneko/nap-proto-core";
import { IndexNode, MsgInfo } from "@/core/packet/transformer/proto";
import { OidbPacket } from "@/core/packet/transformer/base";
import { ImageOcrResult } from "@/core/packet/entities/ocrResult";

export class PacketOperationContext {
private readonly context: PacketContext;

constructor(context: PacketContext) {
this.context = context;
}
Expand Down Expand Up @@ -95,6 +97,46 @@ export class PacketOperationContext {
});
}

async UploadImage(img: PacketMsgPicElement) {
await this.context.highway.uploadImage({
chatType: ChatType.KCHATTYPEC2C,
peerUid: this.context.napcore.basicInfo.uid
}, img);
const index = img.msgInfo?.msgInfoBody?.at(0)?.index;
if (!index) {
throw new Error('img.msgInfo?.msgInfoBody![0].index! is undefined');
}
return await this.GetImageUrl(this.context.napcore.basicInfo.uid, index);
}

async GetImageUrl(selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>) {
const req = trans.DownloadImage.build(selfUid, node);
const resp = await this.context.client.sendOidbPacket(req, true);
const res = trans.DownloadImage.parse(resp);
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
}

async ImageOCR(imgUrl: string) {
const req = trans.ImageOCR.build(imgUrl);
const resp = await this.context.client.sendOidbPacket(req, true);
const res = trans.ImageOCR.parse(resp);
return {
texts: res.ocrRspBody.textDetections.map((item) => {
return {
text: item.detectedText,
confidence: item.confidence,
coordinates: item.polygon.coordinates.map((c) => {
return {
x: c.x,
y: c.y
};
}),
};
}),
language: res.ocrRspBody.language
} as ImageOcrResult;
}

async UploadForwardMsg(msg: PacketMsg[], groupUin: number = 0) {
await this.UploadResources(msg, groupUin);
const req = trans.UploadForwardMsg.build(this.context.napcore.basicInfo.uid, msg, groupUin);
Expand Down
15 changes: 15 additions & 0 deletions src/core/packet/entities/ocrResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface ImageOcrResult {
texts: TextDetection[];
language: string;
}

export interface TextDetection {
text: string;
confidence: number;
coordinates: Coordinate[];
}

export interface Coordinate {
x: number;
y: number;
}
37 changes: 37 additions & 0 deletions src/core/packet/transformer/action/ImageOCR.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as proto from "@/core/packet/transformer/proto";
import { NapProtoMsg } from "@napneko/nap-proto-core";
import { OidbPacket, PacketTransformer } from "@/core/packet/transformer/base";
import OidbBase from "@/core/packet/transformer/oidb/oidbBase";

class ImageOCR extends PacketTransformer<typeof proto.OidbSvcTrpcTcp0xE07_0_Response> {
constructor() {
super();
}

build(url: string): OidbPacket {
const body = new NapProtoMsg(proto.OidbSvcTrpcTcp0xE07_0).encode(
{
version: 1,
client: 0,
entrance: 1,
ocrReqBody: {
imageUrl: url,
originMd5: "",
afterCompressMd5: "",
afterCompressFileSize: "",
afterCompressWeight: "",
afterCompressHeight: "",
isCut: false,
}
}
);
return OidbBase.build(0XEB7, 1, body, false, false);
}

parse(data: Buffer) {
const base = OidbBase.parse(data);
return new NapProtoMsg(proto.OidbSvcTrpcTcp0xE07_0_Response).decode(base.body);
}
}

export default new ImageOCR();
1 change: 1 addition & 0 deletions src/core/packet/transformer/action/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { default as GroupSign } from './GroupSign';
export { default as GetStrangerInfo } from './GetStrangerInfo';
export { default as SendPoke } from './SendPoke';
export { default as SetSpecialTitle } from './SetSpecialTitle';
export { default as ImageOCR } from './ImageOCR';
51 changes: 51 additions & 0 deletions src/core/packet/transformer/highway/DownloadImage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as proto from "@/core/packet/transformer/proto";
import { NapProtoEncodeStructType, NapProtoMsg } from "@napneko/nap-proto-core";
import { OidbPacket, PacketTransformer } from "@/core/packet/transformer/base";
import OidbBase from "@/core/packet/transformer/oidb/oidbBase";
import { IndexNode } from "@/core/packet/transformer/proto";

class DownloadImage extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
constructor() {
super();
}

build(selfUid: string, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
reqHead: {
common: {
requestId: 1,
command: 100
},
scene: {
requestType: 2,
businessType: 1,
sceneType: 1,
c2C: {
accountType: 2,
targetUid: selfUid
},
},
client: {
agentType: 2,
}
},
download: {
node: node,
download: {
video: {
busiType: 0,
sceneType: 0
}
}
}
});
return OidbBase.build(0x11C5, 200, body, true, false);
}

parse(data: Buffer) {
const oidbBody = OidbBase.parse(data).body;
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
}
}

export default new DownloadImage();
1 change: 1 addition & 0 deletions src/core/packet/transformer/highway/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export { default as UploadPrivateFile } from './UploadPrivateFile';
export { default as UploadPrivateImage } from './UploadPrivateImage';
export { default as UploadPrivatePtt } from './UploadPrivatePtt';
export { default as UploadPrivateVideo } from './UploadPrivateVideo';
export { default as DownloadImage } from './DownloadImage';
1 change: 1 addition & 0 deletions src/core/packet/transformer/proto/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ export * from "./oidb/Oidb.0xEB7";
export * from "./oidb/Oidb.0xED3_1";
export * from "./oidb/Oidb.0XFE1_2";
export * from "./oidb/OidbBase";
export * from "./oidb/Oidb.0xE07";
59 changes: 59 additions & 0 deletions src/core/packet/transformer/proto/oidb/Oidb.0xE07.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { ProtoField, ScalarType } from "@napneko/nap-proto-core";

export const OidbSvcTrpcTcp0xE07_0 = {
version: ProtoField(1, ScalarType.UINT32),
client: ProtoField(2, ScalarType.UINT32),
entrance: ProtoField(3, ScalarType.UINT32),
ocrReqBody: ProtoField(10, () => OcrReqBody, true),
};

export const OcrReqBody = {
imageUrl: ProtoField(1, ScalarType.STRING),
languageType: ProtoField(2, ScalarType.UINT32),
scene: ProtoField(3, ScalarType.UINT32),
originMd5: ProtoField(10, ScalarType.STRING),
afterCompressMd5: ProtoField(11, ScalarType.STRING),
afterCompressFileSize: ProtoField(12, ScalarType.STRING),
afterCompressWeight: ProtoField(13, ScalarType.STRING),
afterCompressHeight: ProtoField(14, ScalarType.STRING),
isCut: ProtoField(15, ScalarType.BOOL),
};

export const OidbSvcTrpcTcp0xE07_0_Response = {
retCode: ProtoField(1, ScalarType.INT32),
errMsg: ProtoField(2, ScalarType.STRING),
wording: ProtoField(3, ScalarType.STRING),
ocrRspBody: ProtoField(10, () => OcrRspBody),
};

export const OcrRspBody = {
textDetections: ProtoField(1, () => TextDetection, false, true),
language: ProtoField(2, ScalarType.STRING),
requestId: ProtoField(3, ScalarType.STRING),
ocrLanguageList: ProtoField(101, ScalarType.STRING, false, true),
dstTranslateLanguageList: ProtoField(102, ScalarType.STRING, false, true),
languageList: ProtoField(103, () => Language, false, true),
afterCompressWeight: ProtoField(111, ScalarType.UINT32),
afterCompressHeight: ProtoField(112, ScalarType.UINT32),
};

export const TextDetection = {
detectedText: ProtoField(1, ScalarType.STRING),
confidence: ProtoField(2, ScalarType.UINT32),
polygon: ProtoField(3, () => Polygon),
advancedInfo: ProtoField(4, ScalarType.STRING),
};

export const Polygon = {
coordinates: ProtoField(1, () => Coordinate, false, true),
};

export const Coordinate = {
x: ProtoField(1, ScalarType.INT32),
y: ProtoField(2, ScalarType.INT32),
};

export const Language = {
languageCode: ProtoField(1, ScalarType.STRING),
languageDesc: ProtoField(2, ScalarType.STRING),
};

0 comments on commit c8ee371

Please sign in to comment.