open-vtop 1.0.0 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/captcha-solver.js +68 -16
- package/dist/index.js +4 -1
- package/package.json +8 -4
package/dist/captcha-solver.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CAPTCHA Solver for VTOP
|
|
3
|
-
* Uses
|
|
3
|
+
* Uses pure JS for image processing and bitmap matching / neural network for character recognition
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
5
|
+
import * as jpeg from "jpeg-js";
|
|
6
|
+
import { PNG } from "pngjs";
|
|
6
7
|
import { bitmaps } from "./bitmaps.js";
|
|
7
8
|
// ============ Bitmap-based solver (original method) ============
|
|
8
9
|
function captchaParse(imgarr) {
|
|
@@ -134,6 +135,53 @@ function maxSoft(a) {
|
|
|
134
135
|
}
|
|
135
136
|
return n;
|
|
136
137
|
}
|
|
138
|
+
async function decodeImage(dataUri) {
|
|
139
|
+
const parts = extractDataUriParts(dataUri);
|
|
140
|
+
if (!parts)
|
|
141
|
+
throw new Error("Invalid Data URI");
|
|
142
|
+
const buffer = Buffer.from(parts.base64, "base64");
|
|
143
|
+
if (parts.mimeType === "image/png") {
|
|
144
|
+
return new Promise((resolve, reject) => {
|
|
145
|
+
const png = new PNG();
|
|
146
|
+
png.parse(buffer, (err, data) => {
|
|
147
|
+
if (err)
|
|
148
|
+
reject(err);
|
|
149
|
+
else
|
|
150
|
+
resolve({
|
|
151
|
+
width: data.width,
|
|
152
|
+
height: data.height,
|
|
153
|
+
data: data.data,
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else if (parts.mimeType === "image/jpeg") {
|
|
159
|
+
const decoded = jpeg.decode(buffer, { useTArray: true });
|
|
160
|
+
return {
|
|
161
|
+
width: decoded.width,
|
|
162
|
+
height: decoded.height,
|
|
163
|
+
data: decoded.data,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
throw new Error("Unsupported image type: " + parts.mimeType);
|
|
167
|
+
}
|
|
168
|
+
function resizeImage(src, targetW, targetH) {
|
|
169
|
+
// Nearest neighbor resize
|
|
170
|
+
const target = new Uint8ClampedArray(targetW * targetH * 4);
|
|
171
|
+
for (let y = 0; y < targetH; y++) {
|
|
172
|
+
for (let x = 0; x < targetW; x++) {
|
|
173
|
+
const srcX = Math.floor((x * src.width) / targetW);
|
|
174
|
+
const srcY = Math.floor((y * src.height) / targetH);
|
|
175
|
+
const srcIdx = (srcY * src.width + srcX) * 4;
|
|
176
|
+
const targetIdx = (y * targetW + x) * 4;
|
|
177
|
+
target[targetIdx] = src.data[srcIdx];
|
|
178
|
+
target[targetIdx + 1] = src.data[srcIdx + 1];
|
|
179
|
+
target[targetIdx + 2] = src.data[srcIdx + 2];
|
|
180
|
+
target[targetIdx + 3] = src.data[srcIdx + 3];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return target;
|
|
184
|
+
}
|
|
137
185
|
/**
|
|
138
186
|
* Solve a CAPTCHA image using the saturation-based neural network method
|
|
139
187
|
* @param imgDataUri - Base64 data URI of the captcha image
|
|
@@ -147,12 +195,15 @@ export async function solve(imgDataUri) {
|
|
|
147
195
|
return solveBitmap(imgDataUri);
|
|
148
196
|
}
|
|
149
197
|
const labelTxt = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
198
|
+
const img = await decodeImage(imgDataUri);
|
|
199
|
+
// Target size: 200x40
|
|
200
|
+
let dataToUse = img.data;
|
|
201
|
+
if (img.width !== 200 || img.height !== 40) {
|
|
202
|
+
dataToUse = resizeImage(img, 200, 40);
|
|
203
|
+
}
|
|
204
|
+
// dataToUse is now RGBA array of 200x40 image
|
|
205
|
+
// saturation expects Uint8ClampedArray or similar
|
|
206
|
+
let bls = saturation(dataToUse);
|
|
156
207
|
let out = "";
|
|
157
208
|
for (let i = 0; i < 6; i++) {
|
|
158
209
|
let block = preImg(bls[i]);
|
|
@@ -171,16 +222,17 @@ export async function solve(imgDataUri) {
|
|
|
171
222
|
* @returns Solved captcha string
|
|
172
223
|
*/
|
|
173
224
|
export async function solveBitmap(imgDataUri) {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
const data = imageData.data;
|
|
225
|
+
const img = await decodeImage(imgDataUri);
|
|
226
|
+
// For bitmap solver, we need 180x45
|
|
227
|
+
// Original code: canvas 180x45, drawImage(image, 0, 0, 180, 45)
|
|
228
|
+
// So we definitely need to resize
|
|
229
|
+
const resizedData = resizeImage(img, 180, 45);
|
|
180
230
|
// Convert to grayscale 2D array
|
|
181
231
|
const arr = [];
|
|
182
|
-
for (let i = 0; i <
|
|
183
|
-
const gval =
|
|
232
|
+
for (let i = 0; i < resizedData.length; i += 4) {
|
|
233
|
+
const gval = resizedData[i] * 0.299 +
|
|
234
|
+
resizedData[i + 1] * 0.587 +
|
|
235
|
+
resizedData[i + 2] * 0.114;
|
|
184
236
|
arr.push(Math.round(gval));
|
|
185
237
|
}
|
|
186
238
|
const newArr = [];
|
package/dist/index.js
CHANGED
|
@@ -7,8 +7,11 @@ import { Home } from "./views/Home.js";
|
|
|
7
7
|
import { SuccessMessage } from "./views/partials.js";
|
|
8
8
|
import { sessionManager } from "./session-manager.js";
|
|
9
9
|
const app = new Hono();
|
|
10
|
+
import { createRequire } from "module";
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const htmxPath = require.resolve("htmx.org/dist/htmx.min.js");
|
|
10
13
|
// Serve htmx from node_modules
|
|
11
|
-
app.use("/static/htmx.js", serveStatic({ path:
|
|
14
|
+
app.use("/static/htmx.js", serveStatic({ path: htmxPath }));
|
|
12
15
|
// Main page
|
|
13
16
|
app.get("/", (c) => {
|
|
14
17
|
return c.html(_jsx(Home, {}));
|
package/package.json
CHANGED
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "tsx watch src/index.tsx",
|
|
6
6
|
"build": "tsc",
|
|
7
|
-
"start": "node dist/index.js"
|
|
7
|
+
"start": "node dist/index.js",
|
|
8
|
+
"prepare": "npm run build"
|
|
8
9
|
},
|
|
9
10
|
"dependencies": {
|
|
10
11
|
"@hono/node-server": "^1.19.9",
|
|
11
|
-
"canvas": "^3.2.1",
|
|
12
12
|
"hono": "^4.11.4",
|
|
13
|
-
"htmx.org": "^2.0.8"
|
|
13
|
+
"htmx.org": "^2.0.8",
|
|
14
|
+
"jpeg-js": "^0.4.4",
|
|
15
|
+
"pngjs": "^7.0.0"
|
|
14
16
|
},
|
|
15
17
|
"bin": {
|
|
16
18
|
"open-vtop": "dist/index.js"
|
|
@@ -19,9 +21,11 @@
|
|
|
19
21
|
"dist"
|
|
20
22
|
],
|
|
21
23
|
"devDependencies": {
|
|
24
|
+
"@types/jpeg-js": "^0.3.0",
|
|
22
25
|
"@types/node": "^20.11.17",
|
|
26
|
+
"@types/pngjs": "^6.0.5",
|
|
23
27
|
"tsx": "^4.7.1",
|
|
24
28
|
"typescript": "^5.8.3"
|
|
25
29
|
},
|
|
26
|
-
"version": "1.0.
|
|
30
|
+
"version": "1.0.4"
|
|
27
31
|
}
|