Compare commits

...

12 Commits

Author SHA1 Message Date
d1c5760329 chore: 更新Dockerfile添加镜像更新脚本并运行
All checks were successful
Build Server / Compile DLL (push) Successful in 10m13s
- 更新Dockerfile以添加镜像更新脚本并运行以确保获取最新的镜像。
2025-07-19 15:40:32 +08:00
95b658527a chore: 优化Docker构建流程删除缓存命令
Some checks failed
Build Server / Compile DLL (push) Failing after 2m24s
- 删除清理缓存的命令以优化Docker构建过程。
2025-07-19 15:26:36 +08:00
64347726f0 fix: 修复清理缓存路径中的环境变量问题
Some checks failed
Build Server / Compile DLL (push) Has been cancelled
- 修复清理缓存路径中的环境变量问题以确保脚本有效运行。
2025-07-19 15:25:57 +08:00
b96edf83a5 refactor: 优化容器缓存清理逻辑
Some checks failed
Build Server / Compile DLL (push) Failing after 2m39s
- 优化容器缓存清理逻辑,增加对Pacman和Yay缓存的清除。
2025-07-18 23:02:31 +08:00
97b4c38863 feat: 添加数据验证功能确保图像有效性
Some checks failed
Build Server / Compile DLL (push) Failing after 1m54s
- 在项目依赖中添加了@hono/zod-validator和zod包。
- 添加依赖库"@hono/zod-validator"和"zod"以增强数据验证功能。
- 添加了请求验证以确保提供了有效的图像文件。
2025-07-18 22:50:23 +08:00
7fca8d8df6 Update Dockerfile
All checks were successful
Build Server / Compile DLL (push) Successful in 4m1s
2025-07-09 00:08:05 -04:00
79e1da12e8 refactor: 优化临时文件命名以提高一致性
All checks were successful
Build Server / Compile DLL (push) Successful in 3m56s
- 优化临时文件命名后缀以提高一致性和通用性。
2025-07-08 01:21:49 +08:00
721bd2bb5c 优化Dockerfile,添加清理缓存和临时文件步骤。
All checks were successful
Build Server / Compile DLL (push) Successful in 3m56s
2025-07-08 01:17:25 +08:00
fffa977d18 chore: 简化Docker镜像标签命名格式
All checks were successful
Build Server / Compile DLL (push) Successful in 4m14s
- 调整Docker镜像标签格式,移除架构信息以简化标签命名。
2025-07-08 01:01:16 +08:00
eaa8ae41d5 chore: 增强构建环境功能,添加git安装
All checks were successful
Build Server / Compile DLL (push) Successful in 4m38s
- 添加git安装以增强构建环境功能。
2025-07-08 00:54:52 +08:00
f0eda5ed50 feat: 添加OCR识别功能及镜像优化
Some checks failed
Build Server / Compile DLL (push) Failing after 43s
- 变更基础镜像阶段为运行阶段,同时添加微信二进制包,复制项目文件并安装依赖。
- 添加处理OCR请求的端点以识别上传图像中的文本。
- 将OCR功能封装到WechatOCRClient类中并添加临时文件处理逻辑。
2025-07-08 00:51:06 +08:00
34cf6e9cbf - 使用archlinux:base-devel替换基础镜像并简化文件构建步骤。
- 使用archlinux:base-devel替换基础镜像并简化文件构建步骤。
2025-07-08 00:45:37 +08:00
6 changed files with 104 additions and 67 deletions

View File

@@ -34,4 +34,4 @@ jobs:
context: . context: .
file: Dockerfile file: Dockerfile
push: true push: true
tags: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest-${{ env.ARCH }} tags: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:latest

View File

@@ -1,18 +1,20 @@
# FROM greyltc/archlinux-aur:yay AS lib FROM archlinux:base-devel AS lib
# WORKDIR /src WORKDIR /src
# RUN yay -Syu --noconfirm cmake python nodejs && \ RUN pacman -Syu --noconfirm git cmake python nodejs && \
# git clone https://github.com/swigger/wechat-ocr.git wcocr && \ git clone https://github.com/swigger/wechat-ocr.git wcocr && \
# sed -i 's/v3.21.0/v3.21.2/g' wcocr/CMakeLists.txt && \ mkdir -p wcocr/build && cd wcocr/build && cmake .. && make -j$(nproc)
# mkdir -p wcocr/build && cd wcocr/build && cmake .. && make -j$(nproc)
FROM greyltc/archlinux-aur:yay FROM greyltc/archlinux-aur:yay AS runner
WORKDIR /app WORKDIR /app
COPY . .
COPY --from=lib /src/wcocr/build/libwcocr.so .
# update mirrorlist
ADD https://raw.githubusercontent.com/greyltc/docker-archlinux/master/get-new-mirrors.sh /usr/bin/get-new-mirrors
RUN chmod +x /usr/bin/get-new-mirrors && \
get-new-mirrors
RUN aur-install bun-bin wechat-bin && \ RUN aur-install bun-bin wechat-bin && \
echo "Hello World" bun install --frozen-lockfile
# COPY --from=lib /src/wcocr/build/libwcocr.so .
# COPY src/index.ts .
# RUN bun install hono
# EXPOSE 3000 EXPOSE 3000
# CMD ["bun", "src/index.ts"] CMD ["bun", "src/index.ts"]

View File

@@ -4,7 +4,9 @@
"": { "": {
"name": "wxocr", "name": "wxocr",
"dependencies": { "dependencies": {
"@hono/zod-validator": "^0.7.1",
"hono": "^4.8.4", "hono": "^4.8.4",
"zod": "^4.0.5",
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^4.16.2", "@antfu/eslint-config": "^4.16.2",
@@ -67,6 +69,8 @@
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.3", "", { "dependencies": { "@eslint/core": "^0.15.1", "levn": "^0.4.1" } }, "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag=="], "@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.3", "", { "dependencies": { "@eslint/core": "^0.15.1", "levn": "^0.4.1" } }, "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag=="],
"@hono/zod-validator": ["@hono/zod-validator@0.7.1", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-8+vJT1RvezAx5sN7hiZ5Mis0RMuFL77nBEcqQQgT9ufoLkxr+7ll461WlBJQcGoQSY6EGMClVae19v3s/7bbgQ=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
"@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="], "@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
@@ -633,6 +637,8 @@
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"zod": ["zod@4.0.5", "", {}, "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA=="],
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],

View File

@@ -11,7 +11,9 @@
"typescript": "^5.8.3" "typescript": "^5.8.3"
}, },
"dependencies": { "dependencies": {
"hono": "^4.8.4" "@hono/zod-validator": "^0.7.1",
"hono": "^4.8.4",
"zod": "^4.0.5"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "^4.16.2", "@antfu/eslint-config": "^4.16.2",

View File

@@ -1,7 +1,31 @@
import { zValidator } from '@hono/zod-validator'
import { Hono } from 'hono' import { Hono } from 'hono'
import { Buffer } from 'node:buffer'
import { z } from 'zod'
import { WechatOCRClient } from './wcocr'
const app = new Hono() const app = new Hono()
const client = new WechatOCRClient()
app.get('/', c => c.text('Hello World')) app.post(
'/ocr',
zValidator('form', z.object({
image: z.instanceof(File, { message: 'Image file is required' }),
})),
async (c) => {
const { image } = c.req.valid('form')
const imageBuffer = Buffer.from(await image.arrayBuffer())
try {
const result = await client.recognize(imageBuffer)
return c.json(JSON.parse(result))
} catch (error) {
console.error('OCR processing error:', error)
return c.json({ error: 'Failed to process image' }, 500)
}
},
)
export default app export default app

View File

@@ -2,10 +2,14 @@ import type { Pointer } from 'bun:ffi'
import { CString, dlopen, FFIType, JSCallback, suffix } from 'bun:ffi' import { CString, dlopen, FFIType, JSCallback, suffix } from 'bun:ffi'
import { Buffer } from 'node:buffer' import { Buffer } from 'node:buffer'
import { randomBytes } from 'node:crypto'
import { rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
const libPath = `libwcocr.${suffix}` const libPath = `libwcocr.${suffix}`
const WechatOCR = dlopen(libPath, { const WechatOCRFFI = dlopen(libPath, {
wechat_ocr: { wechat_ocr: {
args: [ args: [
FFIType.cstring, // ocr_exe路径 FFIType.cstring, // ocr_exe路径
@@ -21,12 +25,16 @@ const WechatOCR = dlopen(libPath, {
}, },
}) })
// 主函数 export class WechatOCRClient {
async function callWechatOcr( private ocrExe: string
ocrExe: string, private wechatDir: string
wechatDir: string,
imgfn: string, constructor(options?: { ocrExe?: string, wechatDir?: string }) {
): Promise<string> { this.ocrExe = options?.ocrExe ?? '/opt/wechat/wxocr'
this.wechatDir = options?.wechatDir ?? '/opt/wechat'
}
private async _callWechatOcr(imgfn: string): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 创建回调函数 // 创建回调函数
const callback = new JSCallback( const callback = new JSCallback(
@@ -42,38 +50,33 @@ async function callWechatOcr(
) )
// 调用wechat_ocr函数 // 调用wechat_ocr函数
const result = WechatOCR.symbols.wechat_ocr( const result = WechatOCRFFI.symbols.wechat_ocr(
Buffer.from(`${ocrExe}\0`), // 转换为C字符串 Buffer.from(`${this.ocrExe}\0`), // 转换为C字符串
Buffer.from(`${wechatDir}\0`), Buffer.from(`${this.wechatDir}\0`),
Buffer.from(`${imgfn}\0`), Buffer.from(`${imgfn}\0`),
callback, // 直接传递JSCallback实例 callback, // 直接传递JSCallback实例
) )
if (!result) { if (!result) {
callback.close() callback.close()
reject(new Error('OCR调用失败')) reject(new Error('OCR call failed'))
} }
}) })
} }
async function main() { public async recognize(image: Buffer): Promise<string> {
// 请在这里替换为你的实际路径 const tempPath = join(tmpdir(), `wechat-ocr-${randomBytes(16).toString('hex')}.tmp`)
const ocrExe = '/opt/wechat/wxocr' await writeFile(tempPath, image)
const wechatDir = '/opt/wechat'
const imgfn = '/home/imbytecat/Pictures/Screenshots/Screenshot_07-Jul_10-00-20_9907.png'
console.log('OCR开始...')
try { try {
const result = await callWechatOcr(ocrExe, wechatDir, imgfn) const result = await this._callWechatOcr(tempPath)
console.log('OCR结果:', result) return result
} catch (error) {
console.error('OCR错误:', error)
} finally { } finally {
// 清理资源 await rm(tempPath, { force: true })
WechatOCR.symbols.stop_ocr() }
console.log('OCR结束...') }
public close() {
WechatOCRFFI.symbols.stop_ocr()
} }
} }
// 运行主函数
main()