mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 13:12:17 +08:00
feat: ocr
This commit is contained in:
parent
ffeda0a472
commit
29dfc1590b
15
qqinterface/src/main/java/com/tencent/mobileqq/l0/b/a.java
Normal file
15
qqinterface/src/main/java/com/tencent/mobileqq/l0/b/a.java
Normal 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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
23
qqinterface/src/main/java/com/tencent/mobileqq/l0/b/b.java
Normal file
23
qqinterface/src/main/java/com/tencent/mobileqq/l0/b/b.java
Normal 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;
|
||||||
|
|
||||||
|
}
|
39
qqinterface/src/main/java/com/tencent/mobileqq/l0/b/c.java
Normal file
39
qqinterface/src/main/java/com/tencent/mobileqq/l0/b/c.java
Normal 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;
|
||||||
|
|
||||||
|
}
|
38
qqinterface/src/main/java/com/tencent/mobileqq/ocr/a/a.java
Normal file
38
qqinterface/src/main/java/com/tencent/mobileqq/ocr/a/a.java
Normal 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;
|
||||||
|
}
|
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package com.tencent.mobileqq.ocr.api.impl;
|
||||||
|
|
||||||
|
public class OcrServiceImpl {
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package com.tencent.mobileqq.ocr.api.impl;
|
||||||
|
|
||||||
|
public class PicOcrServiceImpl {
|
||||||
|
}
|
@ -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>>
|
||||||
|
)
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package moe.fuqiuluo.shamrock.remote.api
|
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.ContentType
|
||||||
import io.ktor.http.content.PartData
|
import io.ktor.http.content.PartData
|
||||||
import io.ktor.http.content.forEachPart
|
import io.ktor.http.content.forEachPart
|
||||||
@ -12,7 +14,14 @@ import io.ktor.server.routing.post
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
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.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.CleanCache
|
||||||
import moe.fuqiuluo.shamrock.remote.action.handlers.DownloadFile
|
import moe.fuqiuluo.shamrock.remote.action.handlers.DownloadFile
|
||||||
import moe.fuqiuluo.shamrock.remote.action.handlers.GetDeviceBattery
|
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.action.handlers.UploadFileToShamrock
|
||||||
import moe.fuqiuluo.shamrock.remote.structures.Status
|
import moe.fuqiuluo.shamrock.remote.structures.Status
|
||||||
import moe.fuqiuluo.shamrock.remote.service.config.ShamrockConfig
|
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.asString
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchOrNull
|
import moe.fuqiuluo.shamrock.tools.fetchOrNull
|
||||||
import moe.fuqiuluo.shamrock.tools.fetchOrThrow
|
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.tools.respond
|
||||||
import moe.fuqiuluo.shamrock.utils.FileUtils
|
import moe.fuqiuluo.shamrock.utils.FileUtils
|
||||||
import moe.fuqiuluo.shamrock.utils.MD5
|
import moe.fuqiuluo.shamrock.utils.MD5
|
||||||
|
import moe.fuqiuluo.shamrock.utils.PlatformUtils
|
||||||
|
import mqq.app.MobileQQ
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
fun Routing.otherAction() {
|
fun Routing.otherAction() {
|
||||||
|
|
||||||
if (ShamrockConfig.allowShell()) {
|
if (ShamrockConfig.allowShell()) {
|
||||||
@ -144,4 +158,24 @@ fun Routing.otherAction() {
|
|||||||
ShamrockConfig[key] = value
|
ShamrockConfig[key] = value
|
||||||
respond(true, Status.Ok, "success")
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
@ -37,6 +37,11 @@ fun Routing.fetchRes() {
|
|||||||
call.respondText(GetImage(file), ContentType.Application.Json)
|
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") {
|
getOrPost("/upload_group_file") {
|
||||||
val groupId = fetchOrThrow("group_id").toLong()
|
val groupId = fetchOrThrow("group_id").toLong()
|
||||||
val file = fetchOrThrow("file")
|
val file = fetchOrThrow("file")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user