StarrySkyWorld commited on
Commit
f4d06ad
·
verified ·
1 Parent(s): 78cbf44

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +276 -120
main.py CHANGED
@@ -80,111 +80,132 @@ async def auto_click_task():
80
  if not page:
81
  return
82
 
83
- # 从配置获取要点击的按钮文本列表
84
- click_buttons = config.get("autoClickButtons", ["Got it", "GOT IT", "Accept", "I agree", "OK", "Okay", "Dismiss", "Close"])
85
 
86
  try:
87
  for text in click_buttons:
88
- # 方法1: 使用 getByRole 精确匹配按钮
 
 
89
  try:
90
  btn = page.get_by_role("button", name=text, exact=False)
91
- if await btn.count() > 0 and await btn.first.is_visible(timeout=100):
92
- await btn.first.click(timeout=2000)
93
- print(f'[AutoClick] Clicked "{text}" via getByRole')
94
- return
 
 
95
  except:
96
  pass
97
 
98
- # 方法2: 使用 getByText
99
  try:
100
  btn = page.get_by_text(text, exact=True)
101
- if await btn.count() > 0 and await btn.first.is_visible(timeout=100):
102
- await btn.first.click(timeout=2000)
103
- print(f'[AutoClick] Clicked "{text}" via getByText')
104
- return
 
 
105
  except:
106
  pass
107
 
108
- # 方法3: 各种 CSS 选择器
109
  selectors = [
110
- # CDK Overlay (Angular Material Dialog)
111
- f'.cdk-overlay-container button:has-text("{text}")',
112
- f'.cdk-overlay-container [mat-button]:has-text("{text}")',
113
- f'.cdk-overlay-container [mat-raised-button]:has-text("{text}")',
114
- f'.cdk-overlay-container [mat-flat-button]:has-text("{text}")',
115
- f'.cdk-overlay-container mat-dialog-actions button:has-text("{text}")',
116
- f'.cdk-overlay-container .mat-mdc-dialog-actions button:has-text("{text}")',
117
- f'.cdk-overlay-container .mdc-dialog__actions button:has-text("{text}")',
118
- f'.cdk-overlay-pane button:has-text("{text}")',
119
- f'mat-dialog-container button:has-text("{text}")',
120
- # 标准按钮
121
  f'button:has-text("{text}")',
122
- f'button:text-is("{text}")',
 
123
  f'[role="button"]:has-text("{text}")',
124
- f'a:has-text("{text}")',
125
- # Material 按钮
126
- f'.mat-button:has-text("{text}")',
127
- f'.mat-mdc-button:has-text("{text}")',
128
- f'.mdc-button:has-text("{text}")',
129
- # ms-button (Google 自定义)
130
- f'[ms-button]:has-text("{text}")',
131
- f'.ms-button:has-text("{text}")',
132
- # 通用弹窗
133
- f'[class*="dialog"] button:has-text("{text}")',
134
- f'[class*="modal"] button:has-text("{text}")',
135
  ]
136
-
137
  for selector in selectors:
138
  try:
139
  element = page.locator(selector).first
140
- if await element.is_visible(timeout=100):
141
  await element.click(timeout=2000)
142
- print(f'[AutoClick] Clicked "{text}" via selector: {selector}')
143
  return
144
  except:
145
  continue
146
 
147
- # 方法4: JavaScript 点击
148
  try:
149
- clicked = await page.evaluate(f'''() => {{
150
- const buttons = document.querySelectorAll('button, [role="button"], [mat-button], [ms-button]');
151
- for (const btn of buttons) {{
152
- if (btn.textContent.trim().toLowerCase().includes("{text.lower()}")) {{
153
- btn.click();
154
- return true;
155
- }}
156
- }}
157
- // 检查 CDK overlay
158
- const overlay = document.querySelector('.cdk-overlay-container');
159
- if (overlay) {{
160
- const overlayButtons = overlay.querySelectorAll('button, [role="button"]');
161
- for (const btn of overlayButtons) {{
162
- if (btn.textContent.trim().toLowerCase().includes("{text.lower()}")) {{
163
- btn.click();
164
- return true;
165
- }}
166
- }}
167
- }}
168
- return false;
169
- }}''')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  if clicked:
171
  print(f'[AutoClick] Clicked "{text}" via JavaScript')
172
  return
173
  except:
174
  pass
175
 
176
- # 方法5: 在 iframe 中查找
177
  for frame in page.frames:
178
  if frame == page.main_frame:
179
  continue
180
  try:
181
  btn = frame.get_by_role("button", name=text, exact=False)
182
- if await btn.count() > 0 and await btn.first.is_visible(timeout=100):
183
  await btn.first.click(timeout=2000)
184
  print(f'[AutoClick] Clicked "{text}" in iframe')
185
  return
186
  except:
187
  continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
  except Exception as e:
190
  pass
@@ -342,96 +363,231 @@ async def manual_click(text: str):
342
  return {"status": "error", "message": "Browser not initialized"}
343
 
344
  try:
345
- # 方法1: getByRole
346
- try:
347
- btn = page.get_by_role("button", name=text, exact=False)
348
- if await btn.count() > 0 and await btn.first.is_visible(timeout=1000):
349
- await btn.first.click(timeout=3000)
350
- return {"status": "success", "message": f'Clicked "{text}" via getByRole'}
351
- except:
352
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
 
354
- # 方法2: JavaScript
355
- clicked = await page.evaluate(f'''() => {{
356
- const buttons = document.querySelectorAll('button, [role="button"], [mat-button], [ms-button]');
357
- for (const btn of buttons) {{
358
- if (btn.textContent.trim().toLowerCase().includes("{text.lower()}")) {{
359
- btn.click();
360
- return true;
361
- }}
362
- }}
363
- const overlay = document.querySelector('.cdk-overlay-container');
364
- if (overlay) {{
365
- const overlayButtons = overlay.querySelectorAll('button, [role="button"]');
366
- for (const btn of overlayButtons) {{
367
- if (btn.textContent.trim().toLowerCase().includes("{text.lower()}")) {{
368
- btn.click();
369
- return true;
370
- }}
371
- }}
372
- }}
373
- return false;
374
- }}''')
375
  if clicked:
376
  return {"status": "success", "message": f'Clicked "{text}" via JavaScript'}
377
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  return {"status": "error", "message": f'Button with text "{text}" not found'}
379
  except Exception as e:
380
  return {"status": "error", "message": str(e)}
381
 
382
  @app.get("/debug/buttons")
383
  async def debug_buttons():
384
- """调试:列出页面上所有按钮"""
385
  global page
386
  if not page:
387
  return {"status": "error", "message": "Browser not initialized"}
388
 
389
  try:
390
- # 使用 JavaScript 获取所有按钮,包括 overlay 中的
391
  buttons = await page.evaluate('''() => {
392
  const results = [];
393
- const allButtons = document.querySelectorAll('button, [role="button"], [mat-button], [ms-button]');
394
- allButtons.forEach((btn, i) => {
395
- results.push({
396
- index: i,
397
- text: btn.textContent.trim().substring(0, 50),
398
- visible: btn.offsetParent !== null,
399
- frame: 'main'
400
- });
401
- });
402
- // CDK Overlay
403
- const overlay = document.querySelector('.cdk-overlay-container');
404
- if (overlay) {
405
- const overlayButtons = overlay.querySelectorAll('button, [role="button"]');
406
- overlayButtons.forEach((btn, i) => {
407
  results.push({
408
- index: i,
409
- text: btn.textContent.trim().substring(0, 50),
410
- visible: btn.offsetParent !== null || getComputedStyle(btn).display !== 'none',
411
- frame: 'cdk-overlay'
 
412
  });
413
  });
 
 
 
 
 
 
 
 
414
  }
 
 
415
  return results;
416
  }''')
417
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  return {"status": "success", "count": len(buttons), "buttons": buttons}
419
  except Exception as e:
420
  return {"status": "error", "message": str(e)}
421
 
422
- @app.get("/debug/overlay")
423
- async def debug_overlay():
424
- """调试:检查 CDK Overlay 弹窗内容"""
425
  global page
426
  if not page:
427
  return {"status": "error", "message": "Browser not initialized"}
428
 
429
  try:
430
- html = await page.evaluate('''() => {
431
- const overlay = document.querySelector('.cdk-overlay-container');
432
- return overlay ? overlay.innerHTML : 'No overlay found';
433
- }''')
434
- return {"status": "success", "html": html[:8000]}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
  except Exception as e:
436
  return {"status": "error", "message": str(e)}
437
 
 
80
  if not page:
81
  return
82
 
83
+ click_buttons = config.get("autoClickButtons", ["Got it", "GOT IT", "Accept", "OK", "Dismiss", "Close"])
 
84
 
85
  try:
86
  for text in click_buttons:
87
+ text_lower = text.lower()
88
+
89
+ # 方法1: Playwright getByRole
90
  try:
91
  btn = page.get_by_role("button", name=text, exact=False)
92
+ if await btn.count() > 0:
93
+ first_btn = btn.first
94
+ if await first_btn.is_visible(timeout=200):
95
+ await first_btn.click(timeout=2000)
96
+ print(f'[AutoClick] Clicked "{text}" via getByRole')
97
+ return
98
  except:
99
  pass
100
 
101
+ # 方法2: Playwright getByText
102
  try:
103
  btn = page.get_by_text(text, exact=True)
104
+ if await btn.count() > 0:
105
+ first_btn = btn.first
106
+ if await first_btn.is_visible(timeout=200):
107
+ await first_btn.click(timeout=2000)
108
+ print(f'[AutoClick] Clicked "{text}" via getByText')
109
+ return
110
  except:
111
  pass
112
 
113
+ # 方法3: 使用 locator
114
  selectors = [
 
 
 
 
 
 
 
 
 
 
 
115
  f'button:has-text("{text}")',
116
+ f'text="{text}"',
117
+ f'button >> text="{text}"',
118
  f'[role="button"]:has-text("{text}")',
 
 
 
 
 
 
 
 
 
 
 
119
  ]
 
120
  for selector in selectors:
121
  try:
122
  element = page.locator(selector).first
123
+ if await element.is_visible(timeout=200):
124
  await element.click(timeout=2000)
125
+ print(f'[AutoClick] Clicked "{text}" via locator')
126
  return
127
  except:
128
  continue
129
 
130
+ # 方法4: JavaScript 全局搜索 (包括 Shadow DOM)
131
  try:
132
+ clicked = await page.evaluate('''(textToFind) => {
133
+ function findAndClick(root, text) {
134
+ const lowerText = text.toLowerCase();
135
+
136
+ // 搜索普通元素
137
+ const elements = root.querySelectorAll('button, [role="button"], a, span, div');
138
+ for (const el of elements) {
139
+ const elText = el.textContent || el.innerText || '';
140
+ if (elText.trim().toLowerCase() === lowerText ||
141
+ elText.trim().toLowerCase().includes(lowerText)) {
142
+ // 确保元素可见
143
+ const rect = el.getBoundingClientRect();
144
+ if (rect.width > 0 && rect.height > 0) {
145
+ el.click();
146
+ return true;
147
+ }
148
+ }
149
+ }
150
+
151
+ // 搜索 Shadow DOM
152
+ const allElements = root.querySelectorAll('*');
153
+ for (const el of allElements) {
154
+ if (el.shadowRoot) {
155
+ if (findAndClick(el.shadowRoot, text)) {
156
+ return true;
157
+ }
158
+ }
159
+ }
160
+
161
+ return false;
162
+ }
163
+
164
+ return findAndClick(document, textToFind);
165
+ }''', text)
166
+
167
  if clicked:
168
  print(f'[AutoClick] Clicked "{text}" via JavaScript')
169
  return
170
  except:
171
  pass
172
 
173
+ # 方法5: iframe 中查找
174
  for frame in page.frames:
175
  if frame == page.main_frame:
176
  continue
177
  try:
178
  btn = frame.get_by_role("button", name=text, exact=False)
179
+ if await btn.count() > 0 and await btn.first.is_visible(timeout=200):
180
  await btn.first.click(timeout=2000)
181
  print(f'[AutoClick] Clicked "{text}" in iframe')
182
  return
183
  except:
184
  continue
185
+
186
+ # iframe 内 JavaScript
187
+ try:
188
+ clicked = await frame.evaluate('''(textToFind) => {
189
+ const lowerText = textToFind.toLowerCase();
190
+ const elements = document.querySelectorAll('button, [role="button"], a, span, div');
191
+ for (const el of elements) {
192
+ const elText = el.textContent || el.innerText || '';
193
+ if (elText.trim().toLowerCase().includes(lowerText)) {
194
+ const rect = el.getBoundingClientRect();
195
+ if (rect.width > 0 && rect.height > 0) {
196
+ el.click();
197
+ return true;
198
+ }
199
+ }
200
+ }
201
+ return false;
202
+ }''', text)
203
+
204
+ if clicked:
205
+ print(f'[AutoClick] Clicked "{text}" in iframe via JS')
206
+ return
207
+ except:
208
+ continue
209
 
210
  except Exception as e:
211
  pass
 
363
  return {"status": "error", "message": "Browser not initialized"}
364
 
365
  try:
366
+ # JavaScript 全局搜索点击
367
+ clicked = await page.evaluate('''(textToFind) => {
368
+ function findAndClick(root, text) {
369
+ const lowerText = text.toLowerCase();
370
+ const elements = root.querySelectorAll('button, [role="button"], a, span, div');
371
+ for (const el of elements) {
372
+ const elText = el.textContent || el.innerText || '';
373
+ if (elText.trim().toLowerCase() === lowerText ||
374
+ elText.trim().toLowerCase().includes(lowerText)) {
375
+ const rect = el.getBoundingClientRect();
376
+ if (rect.width > 0 && rect.height > 0) {
377
+ el.click();
378
+ return true;
379
+ }
380
+ }
381
+ }
382
+ const allElements = root.querySelectorAll('*');
383
+ for (const el of allElements) {
384
+ if (el.shadowRoot) {
385
+ if (findAndClick(el.shadowRoot, text)) {
386
+ return true;
387
+ }
388
+ }
389
+ }
390
+ return false;
391
+ }
392
+ return findAndClick(document, textToFind);
393
+ }''', text)
394
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
  if clicked:
396
  return {"status": "success", "message": f'Clicked "{text}" via JavaScript'}
397
 
398
+ # iframe
399
+ for frame in page.frames:
400
+ if frame == page.main_frame:
401
+ continue
402
+ try:
403
+ clicked = await frame.evaluate('''(textToFind) => {
404
+ const lowerText = textToFind.toLowerCase();
405
+ const elements = document.querySelectorAll('button, [role="button"], a, span, div');
406
+ for (const el of elements) {
407
+ const elText = el.textContent || el.innerText || '';
408
+ if (elText.trim().toLowerCase().includes(lowerText)) {
409
+ const rect = el.getBoundingClientRect();
410
+ if (rect.width > 0 && rect.height > 0) {
411
+ el.click();
412
+ return true;
413
+ }
414
+ }
415
+ }
416
+ return false;
417
+ }''', text)
418
+
419
+ if clicked:
420
+ return {"status": "success", "message": f'Clicked "{text}" in iframe'}
421
+ except:
422
+ continue
423
+
424
  return {"status": "error", "message": f'Button with text "{text}" not found'}
425
  except Exception as e:
426
  return {"status": "error", "message": str(e)}
427
 
428
  @app.get("/debug/buttons")
429
  async def debug_buttons():
430
+ """调试:列出所有按钮(包括 Shadow DOM 和 iframe)"""
431
  global page
432
  if not page:
433
  return {"status": "error", "message": "Browser not initialized"}
434
 
435
  try:
436
+ # 主页面按钮
437
  buttons = await page.evaluate('''() => {
438
  const results = [];
439
+
440
+ function findButtons(root, frameName) {
441
+ const elements = root.querySelectorAll('button, [role="button"], a[class*="btn"], [ms-button]');
442
+ elements.forEach((el, i) => {
443
+ const text = (el.textContent || el.innerText || '').trim();
444
+ const rect = el.getBoundingClientRect();
 
 
 
 
 
 
 
 
445
  results.push({
446
+ index: results.length,
447
+ text: text.substring(0, 80),
448
+ visible: rect.width > 0 && rect.height > 0,
449
+ frame: frameName,
450
+ tag: el.tagName.toLowerCase()
451
  });
452
  });
453
+
454
+ // Shadow DOM
455
+ const allElements = root.querySelectorAll('*');
456
+ allElements.forEach(el => {
457
+ if (el.shadowRoot) {
458
+ findButtons(el.shadowRoot, frameName + '-shadow');
459
+ }
460
+ });
461
  }
462
+
463
+ findButtons(document, 'main');
464
  return results;
465
  }''')
466
 
467
+ # iframe 按钮
468
+ for idx, frame in enumerate(page.frames):
469
+ if frame == page.main_frame:
470
+ continue
471
+ try:
472
+ frame_buttons = await frame.evaluate('''(frameIdx) => {
473
+ const results = [];
474
+ const elements = document.querySelectorAll('button, [role="button"], a');
475
+ elements.forEach((el, i) => {
476
+ const text = (el.textContent || el.innerText || '').trim();
477
+ const rect = el.getBoundingClientRect();
478
+ results.push({
479
+ index: i,
480
+ text: text.substring(0, 80),
481
+ visible: rect.width > 0 && rect.height > 0,
482
+ frame: 'iframe-' + frameIdx,
483
+ tag: el.tagName.toLowerCase()
484
+ });
485
+ });
486
+ return results;
487
+ }''', idx)
488
+ buttons.extend(frame_buttons)
489
+ except:
490
+ continue
491
+
492
  return {"status": "success", "count": len(buttons), "buttons": buttons}
493
  except Exception as e:
494
  return {"status": "error", "message": str(e)}
495
 
496
+ @app.get("/debug/search")
497
+ async def debug_search(text: str):
498
+ """调试:搜索包含指定文本的所有元素"""
499
  global page
500
  if not page:
501
  return {"status": "error", "message": "Browser not initialized"}
502
 
503
  try:
504
+ elements = await page.evaluate('''(searchText) => {
505
+ const results = [];
506
+ const lowerText = searchText.toLowerCase();
507
+
508
+ function searchInRoot(root, frameName) {
509
+ const walker = document.createTreeWalker(
510
+ root,
511
+ NodeFilter.SHOW_ELEMENT,
512
+ null,
513
+ false
514
+ );
515
+
516
+ let node;
517
+ while (node = walker.nextNode()) {
518
+ const text = (node.textContent || '').trim();
519
+ if (text.toLowerCase().includes(lowerText)) {
520
+ const rect = node.getBoundingClientRect();
521
+ results.push({
522
+ tag: node.tagName.toLowerCase(),
523
+ text: text.substring(0, 100),
524
+ visible: rect.width > 0 && rect.height > 0,
525
+ frame: frameName,
526
+ className: node.className ? node.className.substring(0, 50) : '',
527
+ id: node.id || ''
528
+ });
529
+ }
530
+
531
+ if (node.shadowRoot) {
532
+ searchInRoot(node.shadowRoot, frameName + '-shadow');
533
+ }
534
+ }
535
+ }
536
+
537
+ searchInRoot(document, 'main');
538
+ return results.slice(0, 50); // 限制数量
539
+ }''', text)
540
+
541
+ # iframe
542
+ for idx, frame in enumerate(page.frames):
543
+ if frame == page.main_frame:
544
+ continue
545
+ try:
546
+ frame_elements = await frame.evaluate('''(searchText) => {
547
+ const results = [];
548
+ const lowerText = searchText.toLowerCase();
549
+ const allElements = document.querySelectorAll('*');
550
+ allElements.forEach(el => {
551
+ const text = (el.textContent || '').trim();
552
+ if (text.toLowerCase().includes(lowerText)) {
553
+ const rect = el.getBoundingClientRect();
554
+ results.push({
555
+ tag: el.tagName.toLowerCase(),
556
+ text: text.substring(0, 100),
557
+ visible: rect.width > 0 && rect.height > 0,
558
+ frame: 'iframe-' + arguments[1],
559
+ className: el.className ? el.className.substring(0, 50) : '',
560
+ id: el.id || ''
561
+ });
562
+ }
563
+ });
564
+ return results.slice(0, 20);
565
+ }''', text, idx)
566
+ elements.extend(frame_elements)
567
+ except:
568
+ continue
569
+
570
+ return {"status": "success", "count": len(elements), "elements": elements}
571
+ except Exception as e:
572
+ return {"status": "error", "message": str(e)}
573
+
574
+ @app.get("/debug/frames")
575
+ async def debug_frames():
576
+ """调试:列出所有 frames"""
577
+ global page
578
+ if not page:
579
+ return {"status": "error", "message": "Browser not initialized"}
580
+
581
+ try:
582
+ frames = []
583
+ for idx, frame in enumerate(page.frames):
584
+ frames.append({
585
+ "index": idx,
586
+ "name": frame.name,
587
+ "url": frame.url,
588
+ "is_main": frame == page.main_frame
589
+ })
590
+ return {"status": "success", "count": len(frames), "frames": frames}
591
  except Exception as e:
592
  return {"status": "error", "message": str(e)}
593