@sovovs/bycli 2.1.52 → 2.1.54

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.
@@ -161,20 +161,79 @@ export const USER_INFO_EXTRACT_SCRIPT = `(() => {
161
161
  }
162
162
  return null;
163
163
  };
164
- const actionNodes = root => Array.from(root.querySelectorAll('a, button, [role="button"]'))
165
- .filter(node => {
166
- if (!visible(node)) return false;
167
- const label = compact(node.textContent || node.getAttribute('aria-label') || node.title);
168
- const hint = [node.className, node.title, node.getAttribute('aria-label')].join(' ');
169
- return label && !/(help|question|icon-question|帮助|说明)/i.test(hint);
170
- });
171
- const action = node => ({
172
- label: compact(node.textContent || node.getAttribute('aria-label') || node.title),
173
- enabled: !(node.disabled === true
174
- || node.getAttribute('aria-disabled') === 'true'
175
- || /(^|\\s)(disabled|is-disabled)(\\s|$)/.test(node.className || '')),
176
- href: node.tagName === 'A' && node.getAttribute('href') ? node.href : null,
177
- });
164
+ const ignoredValueSelector = [
165
+ 'a',
166
+ 'button',
167
+ '[role="button"]',
168
+ '[role="tooltip"]',
169
+ 'svg',
170
+ '.setting_opr',
171
+ '.weui-desktop-setting__status',
172
+ '.setting_status',
173
+ '.status',
174
+ '.frm_tips',
175
+ '.weui-desktop-form__tips',
176
+ '.weui-desktop-setting__desc',
177
+ '.setting_desc',
178
+ '.tips',
179
+ '.desc',
180
+ '.description',
181
+ '.help',
182
+ '.icon-question',
183
+ '[class*="question"]',
184
+ '[class*="help"]',
185
+ '[class*="popover"]',
186
+ '[class*="-ask"]',
187
+ ].join(',');
188
+ const cleanValueText = node => {
189
+ if (!node) return null;
190
+ const copy = node.cloneNode(true);
191
+ copy.querySelectorAll(ignoredValueSelector).forEach(item => item.remove());
192
+ return compact(copy.textContent);
193
+ };
194
+ const primaryValueText = node => {
195
+ if (node && node.matches('.weui-desktop-setting__item__info')) {
196
+ const directText = compact(Array.from(node.childNodes)
197
+ .filter(child => child.nodeType === Node.TEXT_NODE)
198
+ .map(child => child.textContent)
199
+ .join(' '));
200
+ if (directText) return cleanValueText(node);
201
+ const primaryChild = Array.from(node.children)
202
+ .find(child => visible(child) && !child.matches(ignoredValueSelector));
203
+ if (primaryChild) return cleanValueText(primaryChild);
204
+ }
205
+ return cleanValueText(node);
206
+ };
207
+ const textWithout = (node, selector) => {
208
+ if (!node) return null;
209
+ const copy = node.cloneNode(true);
210
+ copy.querySelectorAll(selector).forEach(item => item.remove());
211
+ return compact(copy.textContent);
212
+ };
213
+ const switchValue = row => {
214
+ const node = firstVisible(row, [
215
+ 'input[type="checkbox"]',
216
+ '[role="switch"]',
217
+ '.weui-desktop-switch',
218
+ '.weui-switch',
219
+ '[class*="switch"]',
220
+ ]);
221
+ if (!node) return null;
222
+ const input = node.matches('input[type="checkbox"]')
223
+ ? node
224
+ : node.querySelector('input[type="checkbox"]');
225
+ if (input) return Boolean(input.checked);
226
+ const ariaNode = node.hasAttribute('aria-checked')
227
+ ? node
228
+ : node.querySelector('[aria-checked]');
229
+ const ariaChecked = ariaNode && ariaNode.getAttribute('aria-checked');
230
+ if (ariaChecked === 'true') return true;
231
+ if (ariaChecked === 'false') return false;
232
+ const className = [node.className, node.parentElement && node.parentElement.className].join(' ');
233
+ if (/(^|[\\s_-])(checked|on|active)([\\s_-]|$)/i.test(className)) return true;
234
+ if (/(^|[\\s_-])(unchecked|off)([\\s_-]|$)/i.test(className)) return false;
235
+ return null;
236
+ };
178
237
  const rowSelectors = [
179
238
  '.setting_item',
180
239
  '.weui-desktop-setting__item',
@@ -210,21 +269,55 @@ export const USER_INFO_EXTRACT_SCRIPT = `(() => {
210
269
  const label = compact(heading && heading.textContent)
211
270
  || compact(sectionNode.getAttribute('aria-label'))
212
271
  || '其他信息';
272
+ const authorizationTable = Array.from(sectionNode.querySelectorAll('table'))
273
+ .filter(visible)
274
+ .find(table => {
275
+ const headers = Array.from(table.querySelectorAll('thead th, tr > th'))
276
+ .filter(visible)
277
+ .map(node => compact(node.textContent));
278
+ return headers.includes('第三方平台名称')
279
+ && headers.includes('已授权权限')
280
+ && headers.includes('授权时间');
281
+ }) || null;
282
+ const records = authorizationTable === null ? null : Array.from(
283
+ authorizationTable.querySelectorAll('tbody > tr, tr'),
284
+ ).filter(visible).flatMap(row => {
285
+ const cells = Array.from(row.querySelectorAll(':scope > td')).filter(visible);
286
+ if (cells.length < 3 || row.querySelector('.empty_tips')) return [];
287
+ const nameNode = firstVisible(cells[0], [
288
+ '.plugin_info h4',
289
+ '.plugin_info .name',
290
+ 'h4',
291
+ ]);
292
+ const name = compact(nameNode && nameNode.textContent);
293
+ if (!name) return [];
294
+ const descriptionNode = firstVisible(cells[0], [
295
+ '.plugin_info .desc',
296
+ '.plugin_info p',
297
+ ]);
298
+ const permissions = Array.from(cells[1].querySelectorAll('.privilege'))
299
+ .filter(visible)
300
+ .flatMap(node => {
301
+ const permission = textWithout(node, '.dot');
302
+ return permission ? [permission] : [];
303
+ });
304
+ return [{
305
+ name,
306
+ description: compact(descriptionNode && descriptionNode.textContent),
307
+ permissions,
308
+ authorized_at: cleanValueText(cells[2]),
309
+ }];
310
+ });
213
311
  let rows = Array.from(sectionNode.querySelectorAll(rowSelector)).filter(visible);
214
312
  rows = rows.filter(node => !rows.some(other => other !== node && other.contains(node)));
215
313
  const fields = [];
216
- const actions = [];
217
314
  const seenFields = new Set();
218
- const seenActions = new Set();
219
315
  const fieldRows = new Set();
220
316
  const genericParts = node => Array.from(node.children).flatMap(child => {
221
317
  if (!visible(child)
222
318
  || /^(H1|H2|H3|H4|H5|H6|A|BUTTON)$/.test(child.tagName)
223
319
  || child.getAttribute('role') === 'button') return [];
224
- const copy = child.cloneNode(true);
225
- copy.querySelectorAll('a, button, [role="button"], svg, .setting_opr, .weui-desktop-setting__status, .setting_status, .status')
226
- .forEach(item => item.remove());
227
- const value = compact(copy.textContent);
320
+ const value = cleanValueText(child);
228
321
  return value ? [value] : [];
229
322
  });
230
323
 
@@ -235,56 +328,34 @@ export const USER_INFO_EXTRACT_SCRIPT = `(() => {
235
328
  const labelNode = firstVisible(row, [
236
329
  '.frm_label',
237
330
  '.weui-desktop-setting__label',
331
+ '.weui-desktop-setting__item__label',
238
332
  '.weui-desktop-form__label',
239
333
  'dt',
240
334
  'th',
241
335
  ]);
242
- const fieldLabel = compact(labelNode && labelNode.textContent);
243
- const rowActions = actionNodes(row);
244
- for (const node of rowActions) {
245
- const item = action(node);
246
- const key = JSON.stringify(item);
247
- if (!seenActions.has(key)) {
248
- seenActions.add(key);
249
- actions.push(item);
250
- }
251
- }
336
+ const fieldLabel = cleanValueText(labelNode);
252
337
  if (!fieldLabel) continue;
253
338
  const valueNode = firstVisible(row, [
254
339
  '.weui-desktop-setting__value',
340
+ '.weui-desktop-setting__item__info',
255
341
  '.setting_value',
256
342
  '.frm_value',
257
343
  'dd',
258
344
  'td',
259
345
  '.frm_controls',
260
346
  ]);
261
- const statusNode = firstVisible(row, [
262
- '.weui-desktop-setting__status',
263
- '.setting_status',
264
- '.status',
265
- ]);
266
- let fieldValue = null;
267
- let fieldStatus = compact(statusNode && statusNode.textContent);
268
- if (valueNode) {
269
- const copy = valueNode.cloneNode(true);
270
- copy.querySelectorAll('a, button, [role="button"], .setting_opr, .weui-desktop-setting__status, .setting_status, .status')
271
- .forEach(node => node.remove());
272
- fieldValue = compact(copy.textContent);
273
- }
347
+ let fieldValue = switchValue(row);
348
+ if (fieldValue === null) fieldValue = primaryValueText(valueNode);
274
349
  if (fieldValue === null) {
275
350
  const parts = genericParts(row);
276
351
  const labelIndex = parts.indexOf(fieldLabel);
277
352
  if (labelIndex !== -1) {
278
353
  fieldValue = parts[labelIndex + 1] || null;
279
- if (fieldStatus === null && parts.length > labelIndex + 2) {
280
- fieldStatus = parts.slice(labelIndex + 2).join(' ');
281
- }
282
354
  }
283
355
  }
284
356
  const item = {
285
357
  label: fieldLabel,
286
358
  value: fieldValue,
287
- status: fieldStatus,
288
359
  };
289
360
  const key = JSON.stringify(item);
290
361
  if (!seenFields.has(key)) {
@@ -305,7 +376,6 @@ export const USER_INFO_EXTRACT_SCRIPT = `(() => {
305
376
  const item = {
306
377
  label: parts[0],
307
378
  value: parts[1] || null,
308
- status: parts.length > 2 ? parts.slice(2).join(' ') : null,
309
379
  };
310
380
  const key = JSON.stringify(item);
311
381
  if (!seenFields.has(key)) {
@@ -314,19 +384,12 @@ export const USER_INFO_EXTRACT_SCRIPT = `(() => {
314
384
  }
315
385
  }
316
386
 
317
- const rowSet = new Set(rows);
318
- for (const node of actionNodes(sectionNode)) {
319
- if (rows.some(row => rowSet.has(row) && row.contains(node))) continue;
320
- const item = action(node);
321
- const key = JSON.stringify(item);
322
- if (!seenActions.has(key)) {
323
- seenActions.add(key);
324
- actions.push(item);
325
- }
387
+ if (fields.length > 0 || (records && records.length > 0)) {
388
+ return [{ label, fields, ...(records === null ? {} : { records }) }];
326
389
  }
327
- if (fields.length > 0 || actions.length > 0) return [{ label, fields, actions }];
390
+ if (records !== null) return [{ label, fields, records, empty: true }];
328
391
  const empty = Boolean(sectionNode.querySelector('table thead th, table tr > th'));
329
- return empty ? [{ label, fields, actions, empty: true }] : [];
392
+ return empty ? [{ label, fields, empty: true }] : [];
330
393
  });
331
394
 
332
395
  if (sections.length === 0 && /(无权限|暂无权限|暂不支持|不可用)/.test(document.body.textContent || '')) {
@@ -342,20 +405,26 @@ function normalizeField(field, tabLabel, sectionIndex, fieldIndex) {
342
405
  execution(label, prefix);
343
406
  return {
344
407
  label,
345
- value: text(field.value),
346
- status: text(field.status),
408
+ value: typeof field.value === 'boolean' ? field.value : text(field.value),
347
409
  };
348
410
  }
349
411
 
350
- function normalizeAction(action, tabLabel, sectionIndex, actionIndex) {
351
- const prefix = `WeChat ${tabLabel} returned an invalid action at section ${sectionIndex} index ${actionIndex}`;
352
- execution(object(action), prefix);
353
- const label = text(action.label);
354
- execution(label, prefix);
412
+ function normalizeAuthorizationRecord(record, tabLabel, sectionIndex, recordIndex) {
413
+ const prefix = `WeChat ${tabLabel} returned an invalid authorization record at section ${sectionIndex} index ${recordIndex}`;
414
+ execution(object(record), prefix);
415
+ const name = text(record.name);
416
+ const authorizedAt = text(record.authorized_at);
417
+ execution(name && authorizedAt && Array.isArray(record.permissions), prefix);
418
+ const permissions = record.permissions.map((permission, permissionIndex) => {
419
+ const normalized = text(permission);
420
+ execution(normalized, `${prefix} permission ${permissionIndex}`);
421
+ return normalized;
422
+ });
355
423
  return {
356
- label,
357
- enabled: action.enabled !== false,
358
- path: sanitizeActionPath(action.href ?? action.path),
424
+ name,
425
+ description: text(record.description),
426
+ permissions,
427
+ authorized_at: authorizedAt,
359
428
  };
360
429
  }
361
430
 
@@ -375,16 +444,18 @@ export function normalizeUserInfoTab(tabId, payload) {
375
444
  const label = text(section.label);
376
445
  execution(label, prefix);
377
446
  const rawFields = section.fields ?? [];
378
- const rawActions = section.actions ?? [];
379
- execution(Array.isArray(rawFields) && Array.isArray(rawActions), prefix);
447
+ execution(Array.isArray(rawFields), prefix);
380
448
  const fields = rawFields.map((field, fieldIndex) => (
381
449
  normalizeField(field, definition.label, sectionIndex, fieldIndex)
382
450
  ));
383
- const actions = rawActions.map((action, actionIndex) => (
384
- normalizeAction(action, definition.label, sectionIndex, actionIndex)
451
+ const hasRecords = Object.prototype.hasOwnProperty.call(section, 'records');
452
+ const rawRecords = hasRecords ? section.records : [];
453
+ execution(Array.isArray(rawRecords), prefix);
454
+ const records = rawRecords.map((record, recordIndex) => (
455
+ normalizeAuthorizationRecord(record, definition.label, sectionIndex, recordIndex)
385
456
  ));
386
- return fields.length > 0 || actions.length > 0 || section.empty === true
387
- ? [{ label, fields, actions }]
457
+ return fields.length > 0 || records.length > 0 || section.empty === true
458
+ ? [{ label, fields, ...(hasRecords ? { records } : {}) }]
388
459
  : [];
389
460
  });
390
461
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sovovs/bycli",
3
- "version": "2.1.52",
3
+ "version": "2.1.54",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },