feat: ocr

This commit is contained in:
ikechan8370 2024-07-07 17:51:51 +08:00
parent ffeda0a472
commit 29dfc1590b
10 changed files with 306 additions and 0 deletions

View File

@ -0,0 +1,15 @@
package com.tencent.mobileqq.l0.b;
public class a {
public void b () {
}
public void c (c cVar) {
}
public void onUpdate(int progress, boolean z, Object obj) {
}
}

View File

@ -0,0 +1,23 @@
package com.tencent.mobileqq.l0.b;
import android.graphics.Point;
import java.util.ArrayList;
public class b {
// text
public String a;
// confidence
public int b;
// coordinates
public ArrayList<Point> c;
/* renamed from: d */
public int d;
/* renamed from: e */
public boolean e;
}

View File

@ -0,0 +1,39 @@
package com.tencent.mobileqq.l0.b;
import java.util.ArrayList;
import java.util.HashMap;
public class c {
// image
public String a;
// width
public int b;
// height
public int c;
// lang
public String d;
// url
public String e;
// results
public ArrayList<b> f;
public ArrayList<String> g;
public HashMap<String, String> h;
public int i;
public int j;
public int k;
public String l;
public int m;
}

View File

@ -0,0 +1,38 @@
package com.tencent.mobileqq.ocr.a;
public class a {
public String a;
// 1
public int b;
// file location
public String c;
// null
public String d;
// 0
public long e;
// md5
public String f;
// null
public String g;
// false
public boolean h;
// 0
public int i;
// 0
public int j;
// 0
public long k;
// null
public String l;
}

View File

@ -0,0 +1,10 @@
package com.tencent.mobileqq.ocr.api;
import mqq.app.api.IRuntimeService;
public interface IPicOcrService extends IRuntimeService {
void putOcrResult(String str, com.tencent.mobileqq.l0.b.c cVar);
void requestOcr(com.tencent.mobileqq.ocr.a.a aVar, com.tencent.mobileqq.l0.b.a callback);
}

View File

@ -0,0 +1,4 @@
package com.tencent.mobileqq.ocr.api.impl;
public class OcrServiceImpl {
}

View File

@ -0,0 +1,4 @@
package com.tencent.mobileqq.ocr.api.impl;
public class PicOcrServiceImpl {
}

View File

@ -0,0 +1,134 @@
package moe.fuqiuluo.shamrock.remote.action.handlers
import com.tencent.mobileqq.l0.b.c
import com.tencent.mobileqq.ocr.a.a
import com.tencent.mobileqq.ocr.api.IPicOcrService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MD5
import moe.fuqiuluo.symbols.OneBotHandler
import java.io.File
import kotlin.coroutines.resume
@OneBotHandler("ocr_image")
internal object OcrImage: IActionHandler() {
override suspend fun internalHandle(session: ActionSession): String {
val image = session.getString("image")
return invoke(image, session.echo)
}
suspend operator fun invoke(image: String, echo: JsonElement = EmptyJsonString): String {
val file = FileUtils.getFile(image).let {
if (!it.exists()) {
return@let FileUtils.parseAndSave(image)
}
it
}
LogCenter.log("${file.absoluteFile.absolutePath}进行OCR", Level.DEBUG)
val retryCount = 10
val delayMillis = 1000L
repeat(retryCount) {
val result = getOcrResult(file)
if (result.isSuccess) {
return ok(result.getOrNull(), echo)
} else if (result.isFailure && result.exceptionOrNull()?.message == "no cache") {
withContext(Dispatchers.IO) {
Thread.sleep(delayMillis)
}
} else {
return error(result.exceptionOrNull()?.message ?: "", echo)
}
}
return error("ocr failed", echo)
}
private suspend fun getOcrResult(file: File): Result<OcrResult> {
val ocrService = BaseSvc.app.getRuntimeService(IPicOcrService::class.java, "all")
?: return Result.failure(Error("获取OCR服务失败"))
return withTimeoutOrNull(5000) {
suspendCancellableCoroutine { continuation ->
ocrService.requestOcr(
a().apply {
this.b = 1
this.c = file.absolutePath
this.f = MD5.genFileMd5Hex(file.absolutePath)
}, object : com.tencent.mobileqq.l0.b.a() {
override fun b() {
// call uploadOcrPic and then call b
continuation.resume(Result.failure(Error("no cache")))
}
override fun c(result: c?) {
continuation.resume(Result.success(OcrResult(
texts = result?.f?.map {
TextDetection(
text = it.a,
confidence = it.b,
coordinates = it.c.map {
ArrayList<Int>().apply {
add(it.x)
add(it.y)
}
}
)
}!!,
language = result.h[result.d] ?: result.d,
url = result.e
)))
}
override fun onUpdate(i2: Int, z: Boolean, obj: Any?) {
LogCenter.log("$i2, $z, ${obj.toString()}")
if (i2 == 100) {
val result = obj as c
continuation.resume(Result.success(OcrResult(
texts = result.f.map {
TextDetection(
text = it.a,
confidence = it.b,
coordinates = it.c.map {
ArrayList<Int>().apply {
add(it.x)
add(it.y)
}
}
)
},
language = result.h[result.d] ?: result.d,
url = result.e
)))
}
}
})
}
} ?: Result.failure(Exception("OCR timed out"))
}
override val requiredParams: Array<String> = arrayOf("group_id", "user_id")
@Serializable
data class OcrResult (
val texts: List<TextDetection>,
val language: String,
val url: String
)
@Serializable
data class TextDetection (
val text: String,
val confidence: Int,
val coordinates: List<List<Int>>
)
}

View File

@ -1,5 +1,7 @@
package moe.fuqiuluo.shamrock.remote.api
import com.tencent.mobileqq.dt.app.Dtc
import com.tencent.mobileqq.dt.model.FEBound
import io.ktor.http.ContentType
import io.ktor.http.content.PartData
import io.ktor.http.content.forEachPart
@ -12,7 +14,14 @@ import io.ktor.server.routing.post
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.remote.action.handlers.CleanCache
import moe.fuqiuluo.shamrock.remote.action.handlers.DownloadFile
import moe.fuqiuluo.shamrock.remote.action.handlers.GetDeviceBattery
@ -21,6 +30,8 @@ import moe.fuqiuluo.shamrock.remote.action.handlers.RestartMe
import moe.fuqiuluo.shamrock.remote.action.handlers.UploadFileToShamrock
import moe.fuqiuluo.shamrock.remote.structures.Status
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
import moe.fuqiuluo.shamrock.remote.structures.CommonResult
import moe.fuqiuluo.shamrock.tools.GlobalJson5
import moe.fuqiuluo.shamrock.tools.asString
import moe.fuqiuluo.shamrock.tools.fetchOrNull
import moe.fuqiuluo.shamrock.tools.fetchOrThrow
@ -32,8 +43,11 @@ import moe.fuqiuluo.shamrock.tools.json
import moe.fuqiuluo.shamrock.tools.respond
import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MD5
import moe.fuqiuluo.shamrock.utils.PlatformUtils
import mqq.app.MobileQQ
import java.io.File
@OptIn(ExperimentalSerializationApi::class)
fun Routing.otherAction() {
if (ShamrockConfig.allowShell()) {
@ -144,4 +158,24 @@ fun Routing.otherAction() {
ShamrockConfig[key] = value
respond(true, Status.Ok, "success")
}
getOrPost("/dt") {
val version = PlatformUtils.getQQVersionCode()
val qua = PlatformUtils.getQUA()
call.respondText(Json.encodeToString(
buildJsonObject {
put("qua", JsonPrimitive(qua))
put("version", JsonPrimitive(version))
}
), ContentType.Application.Json)
}
getOrPost("/mmkv") {
val key = fetchOrThrow("key")
call.respondText(Json.encodeToString(
buildJsonObject {
put("value", JsonPrimitive(Dtc.mmKVValue(key)))
}
), ContentType.Application.Json)
}
}

View File

@ -37,6 +37,11 @@ fun Routing.fetchRes() {
call.respondText(GetImage(file), ContentType.Application.Json)
}
getOrPost("/ocr_image") {
val file = fetchOrThrow("image")
call.respondText(OcrImage(file), ContentType.Application.Json)
}
getOrPost("/upload_group_file") {
val groupId = fetchOrThrow("group_id").toLong()
val file = fetchOrThrow("file")