koishi-plugin-bns-blacklist 1.0.7 → 1.0.9
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/lib/index.js +72 -9
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -38,6 +38,7 @@ exports.apply = apply;
|
|
|
38
38
|
const koishi_1 = require("koishi");
|
|
39
39
|
const fs = __importStar(require("fs"));
|
|
40
40
|
const path = __importStar(require("path"));
|
|
41
|
+
const zlib = __importStar(require("zlib"));
|
|
41
42
|
exports.name = 'bns-blacklist';
|
|
42
43
|
exports.inject = ['puppeteer'];
|
|
43
44
|
exports.Config = koishi_1.Schema.object({
|
|
@@ -227,23 +228,43 @@ async function scrapeSheet(page, url, logger) {
|
|
|
227
228
|
const text = item.text || item.body;
|
|
228
229
|
if (logger)
|
|
229
230
|
logger.info(` ${item.endpoint} -> status=${item.status}, len=${text?.length}`);
|
|
231
|
+
if (!text || text.length < 10)
|
|
232
|
+
continue;
|
|
233
|
+
// Try to parse Tencent Docs custom key-value format
|
|
234
|
+
const kvData = parseTencentKvFormat(text, logger);
|
|
235
|
+
if (kvData) {
|
|
236
|
+
// Look for chunk data (compressed cell data)
|
|
237
|
+
const chunkKey = Object.keys(kvData).find(k => k.startsWith('chunk_'));
|
|
238
|
+
if (chunkKey) {
|
|
239
|
+
try {
|
|
240
|
+
const compressed = Buffer.from(kvData[chunkKey], 'base64');
|
|
241
|
+
const decompressed = zlib.inflateSync(compressed).toString('utf-8');
|
|
242
|
+
if (logger)
|
|
243
|
+
logger.info(` Chunk decompressed: ${decompressed.length} bytes, sample: ${decompressed.substring(0, 300)}`);
|
|
244
|
+
const rows = parseTencentChunk(decompressed);
|
|
245
|
+
if (rows.length > 0) {
|
|
246
|
+
if (logger)
|
|
247
|
+
logger.info(`Found ${rows.length} rows from chunk`);
|
|
248
|
+
return parseSheetData(rows);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch (e) {
|
|
252
|
+
if (logger)
|
|
253
|
+
logger.info(` Chunk decompress failed: ${e.message}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Try JSON parse as fallback
|
|
230
258
|
try {
|
|
231
259
|
const json = JSON.parse(text);
|
|
232
|
-
// Log sample of JSON structure for debugging
|
|
233
|
-
if (logger && text.length > 100) {
|
|
234
|
-
const keys = Object.keys(json);
|
|
235
|
-
logger.info(` JSON keys: [${keys.join(', ')}]`);
|
|
236
|
-
// Log first 500 chars
|
|
237
|
-
logger.info(` JSON sample: ${text.substring(0, 500)}`);
|
|
238
|
-
}
|
|
239
260
|
const rows = extractRowsFromJson(json);
|
|
240
261
|
if (rows.length > 0) {
|
|
241
262
|
if (logger)
|
|
242
|
-
logger.info(`Found data: ${rows.length} rows`);
|
|
263
|
+
logger.info(`Found data: ${rows.length} rows via JSON`);
|
|
243
264
|
return parseSheetData(rows);
|
|
244
265
|
}
|
|
245
266
|
}
|
|
246
|
-
catch (_) { /*
|
|
267
|
+
catch (_) { /* not JSON */ }
|
|
247
268
|
}
|
|
248
269
|
// Strategy 2: Dump page text and try to parse
|
|
249
270
|
const pageText = await page.evaluate(() => document.body.innerText);
|
|
@@ -267,6 +288,48 @@ async function scrapeSheet(page, url, logger) {
|
|
|
267
288
|
}
|
|
268
289
|
return [];
|
|
269
290
|
}
|
|
291
|
+
// Parse Tencent Docs custom KV text format:
|
|
292
|
+
// key \n type \n len \n value (each on separate lines)
|
|
293
|
+
function parseTencentKvFormat(text, logger) {
|
|
294
|
+
const parts = text.split('\n').map(s => s.trim()).filter(s => s.length > 0);
|
|
295
|
+
const result = {};
|
|
296
|
+
let i = 0;
|
|
297
|
+
while (i < parts.length - 2) {
|
|
298
|
+
const key = parts[i];
|
|
299
|
+
const type = parts[i + 1];
|
|
300
|
+
// type is usually "text", value length is next, then value
|
|
301
|
+
if (type === 'text' && /^\d+$/.test(parts[i + 2])) {
|
|
302
|
+
const len = parseInt(parts[i + 2]);
|
|
303
|
+
const value = parts[i + 3] || '';
|
|
304
|
+
result[key] = value;
|
|
305
|
+
i += 4;
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
i += 1;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return Object.keys(result).length > 2 ? result : null;
|
|
312
|
+
}
|
|
313
|
+
// Parse the decompressed chunk data into rows
|
|
314
|
+
// The chunk format is likely tab-separated or similar
|
|
315
|
+
function parseTencentChunk(data) {
|
|
316
|
+
const rows = [];
|
|
317
|
+
const lines = data.split('\n').filter(l => l.trim());
|
|
318
|
+
for (const line of lines) {
|
|
319
|
+
// Try tab-separated
|
|
320
|
+
const cells = line.split('\t');
|
|
321
|
+
if (cells.length >= 3) {
|
|
322
|
+
rows.push(cells.map(c => c.trim()));
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
// Try comma-separated
|
|
326
|
+
const csvCells = line.split(',');
|
|
327
|
+
if (csvCells.length >= 5) {
|
|
328
|
+
rows.push(csvCells.map(c => c.trim().replace(/^"|"$/g, '')));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return rows;
|
|
332
|
+
}
|
|
270
333
|
// Recursively search JSON for array-of-arrays that looks like sheet data
|
|
271
334
|
function extractRowsFromJson(obj, depth = 0) {
|
|
272
335
|
if (depth > 8)
|