koishi-plugin-bns-blacklist 1.0.5 → 1.0.7
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 +65 -50
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -184,73 +184,88 @@ ${itemsHtml}
|
|
|
184
184
|
}
|
|
185
185
|
// ─── Data Scraping ─────────────────────────────────────────
|
|
186
186
|
async function scrapeSheet(page, url, logger) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
187
|
+
await page.goto(url, { waitUntil: 'networkidle2', timeout: 60000 });
|
|
188
|
+
// Wait for the sheet to fully render
|
|
189
|
+
await new Promise(r => setTimeout(r, 10000));
|
|
190
|
+
// Log page info for debugging
|
|
191
|
+
if (logger) {
|
|
192
|
+
const title = await page.title();
|
|
193
|
+
const bodyLen = await page.evaluate(() => document.body.innerText.length);
|
|
194
|
+
logger.info(`Page title: "${title}", body text length: ${bodyLen}`);
|
|
195
|
+
}
|
|
196
|
+
// Strategy 1: Use the page's own fetch to call Tencent Docs internal API
|
|
197
|
+
const apiData = await page.evaluate(async () => {
|
|
198
|
+
const results = [];
|
|
199
|
+
// Try common Tencent Docs API endpoints from within the authenticated page
|
|
200
|
+
const baseUrl = 'https://docs.qq.com';
|
|
201
|
+
const sheetId = 'DZXRuRmtTS21mWU9V';
|
|
202
|
+
const endpoints = [
|
|
203
|
+
`/dop-api/sheet/data?id=${sheetId}`,
|
|
204
|
+
`/dop-api/sheet/get?id=${sheetId}`,
|
|
205
|
+
`/dop-api/spreadsheet/${sheetId}`,
|
|
206
|
+
`/api/sheet/data?id=${sheetId}`,
|
|
207
|
+
`/api/sheet/${sheetId}/values`,
|
|
208
|
+
`/openapi/v1/spreadsheet/${sheetId}/values/Sheet1`,
|
|
209
|
+
`/v2/sheet/${sheetId}/export?format=json`,
|
|
210
|
+
`/cgi-bin/online_docs/sheet_get?docId=${sheetId}`,
|
|
211
|
+
];
|
|
212
|
+
for (const endpoint of endpoints) {
|
|
193
213
|
try {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
if (
|
|
197
|
-
|
|
214
|
+
const resp = await fetch(baseUrl + endpoint, { credentials: 'include' });
|
|
215
|
+
const text = await resp.text();
|
|
216
|
+
if (text.length > 50 && !text.includes('Not Found') && !text.includes('<html')) {
|
|
217
|
+
results.push({ endpoint, text, status: resp.status });
|
|
198
218
|
}
|
|
199
219
|
}
|
|
200
|
-
catch (_) { /*
|
|
220
|
+
catch (_) { /* skip */ }
|
|
201
221
|
}
|
|
222
|
+
return results;
|
|
202
223
|
});
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
224
|
+
if (logger)
|
|
225
|
+
logger.info(`API endpoints tried: ${apiData.length} responded`);
|
|
226
|
+
for (const item of apiData) {
|
|
227
|
+
const text = item.text || item.body;
|
|
228
|
+
if (logger)
|
|
229
|
+
logger.info(` ${item.endpoint} -> status=${item.status}, len=${text?.length}`);
|
|
208
230
|
try {
|
|
209
|
-
const json = JSON.parse(
|
|
231
|
+
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
|
+
}
|
|
210
239
|
const rows = extractRowsFromJson(json);
|
|
211
240
|
if (rows.length > 0) {
|
|
212
241
|
if (logger)
|
|
213
|
-
logger.info(`Found data
|
|
242
|
+
logger.info(`Found data: ${rows.length} rows`);
|
|
214
243
|
return parseSheetData(rows);
|
|
215
244
|
}
|
|
216
245
|
}
|
|
217
246
|
catch (_) { /* try next */ }
|
|
218
247
|
}
|
|
219
|
-
//
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
});
|
|
231
|
-
// Look for data in common global stores
|
|
232
|
-
for (const key of ['__app', 'app', '__SHEET_APP__', '__INITIAL_STATE__', '__NUXT__', '__NEXT_DATA__']) {
|
|
233
|
-
const obj = window[key];
|
|
234
|
-
if (!obj)
|
|
235
|
-
continue;
|
|
236
|
-
const jsonStr = JSON.stringify(obj);
|
|
237
|
-
const found = findSheetRowsInJson(jsonStr);
|
|
238
|
-
if (found.length > 0)
|
|
239
|
-
return found;
|
|
248
|
+
// Strategy 2: Dump page text and try to parse
|
|
249
|
+
const pageText = await page.evaluate(() => document.body.innerText);
|
|
250
|
+
if (pageText.length > 100) {
|
|
251
|
+
const lines = pageText.split('\n').filter((l) => l.trim());
|
|
252
|
+
// Look for data lines that have at least 3 tab-separated fields
|
|
253
|
+
const tabLines = lines.filter((l) => l.split('\t').length >= 3);
|
|
254
|
+
if (tabLines.length >= 2) {
|
|
255
|
+
const rows = tabLines.map((l) => l.split('\t'));
|
|
256
|
+
if (logger)
|
|
257
|
+
logger.info(`Parsed ${rows.length} rows from page text`);
|
|
258
|
+
return parseSheetData(rows);
|
|
240
259
|
}
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const cells = line.split('\t');
|
|
247
|
-
if (cells.length >= 3)
|
|
248
|
-
results.push(cells);
|
|
260
|
+
// If no tabs, maybe data is in a different format - log first few lines
|
|
261
|
+
if (logger) {
|
|
262
|
+
logger.info(`Page text first 5 lines (${lines.length} total):`);
|
|
263
|
+
for (let i = 0; i < Math.min(5, lines.length); i++) {
|
|
264
|
+
logger.info(` [${i}] ${lines[i].substring(0, 200)}`);
|
|
249
265
|
}
|
|
250
266
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
return parseSheetData(fallbackData);
|
|
267
|
+
}
|
|
268
|
+
return [];
|
|
254
269
|
}
|
|
255
270
|
// Recursively search JSON for array-of-arrays that looks like sheet data
|
|
256
271
|
function extractRowsFromJson(obj, depth = 0) {
|