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.
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * CAPTCHA Solver for VTOP
3
- * Uses canvas for image processing and bitmap matching / neural network for character recognition
3
+ * Uses pure JS for image processing and bitmap matching / neural network for character recognition
4
4
  */
5
- import { createCanvas, loadImage } from "canvas";
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 canvas = createCanvas(200, 40);
151
- const ctx = canvas.getContext("2d");
152
- const image = await loadImage(imgDataUri);
153
- ctx.drawImage(image, 0, 0, 200, 40);
154
- const pd = ctx.getImageData(0, 0, 200, 40);
155
- let bls = saturation(pd.data);
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 canvas = createCanvas(180, 45);
175
- const ctx = canvas.getContext("2d");
176
- const image = await loadImage(imgDataUri);
177
- ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
178
- const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
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 < data.length; i += 4) {
183
- const gval = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114;
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: "./node_modules/htmx.org/dist/htmx.min.js" }));
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.0"
30
+ "version": "1.0.4"
27
31
  }