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.
Files changed (2) hide show
  1. package/lib/index.js +65 -50
  2. 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
- // Collect network responses that might contain sheet data
188
- const capturedData = [];
189
- page.on('response', async (response) => {
190
- const respUrl = response.url();
191
- // Tencent Docs sheet data APIs
192
- if (respUrl.includes('/api/') || respUrl.includes('/dop-api/') || respUrl.includes('opendoc')) {
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 body = await response.text();
195
- // Only capture JSON responses that look like sheet data
196
- if (body.length > 100 && body.length < 500000) {
197
- capturedData.push({ url: respUrl, body });
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 (_) { /* ignore */ }
220
+ catch (_) { /* skip */ }
201
221
  }
222
+ return results;
202
223
  });
203
- await page.goto(url, { waitUntil: 'networkidle2', timeout: 60000 });
204
- // Wait additional time for async data loading
205
- await new Promise(r => setTimeout(r, 8000));
206
- // Try to find the sheet data in captured API responses
207
- for (const item of capturedData) {
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(item.body);
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 via API: ${item.url} (${rows.length} rows)`);
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
- // Fallback: try to extract from page context
220
- const fallbackData = await page.evaluate(() => {
221
- const results = [];
222
- // Try to find Tencent Docs internal data stores
223
- const allKeys = Object.keys(window).filter(k => {
224
- try {
225
- return window[k] && typeof window[k] === 'object';
226
- }
227
- catch (_) {
228
- return false;
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
- // Last resort: all visible text
242
- const body = document.body.innerText;
243
- if (body.length > 100) {
244
- const lines = body.split('\n').filter((l) => l.trim());
245
- for (const line of lines) {
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
- return results;
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-bns-blacklist",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "剑灵怀旧服副本黑名单查询 - 从腾讯文档实时拉取数据,输入角色名即可生成黑名单截图",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",