fix: click avatar/author via element handles, prevent wild scrolling
This commit is contained in:
parent
2caffab1fa
commit
00bbd196bf
@ -6,6 +6,15 @@ from playwright.sync_api import Page
|
|||||||
|
|
||||||
from teampulse.monitor import _PROFILE_EMAIL_SELECTOR
|
from teampulse.monitor import _PROFILE_EMAIL_SELECTOR
|
||||||
|
|
||||||
|
# Selectors for the clickable sender element (avatar or name) that opens the profile card
|
||||||
|
_AUTHOR_SELECTORS = [
|
||||||
|
# Channel meeting: avatar next to reply-message-header name
|
||||||
|
"[data-tid='reply-message-header-avatar']",
|
||||||
|
"[data-tid='post-message-header-avatar']",
|
||||||
|
# Meeting chat: author name element
|
||||||
|
"[data-tid='message-author-name']",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class Resolver:
|
class Resolver:
|
||||||
def __init__(self, cache_path: Path, page: Page):
|
def __init__(self, cache_path: Path, page: Page):
|
||||||
@ -36,40 +45,57 @@ class Resolver:
|
|||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
def _extract_email_from_profile(self, display_name: str) -> str | None:
|
def _find_and_click_sender(self, display_name: str) -> bool:
|
||||||
# Use Playwright's native click (real mouse events) to trigger the Teams profile card.
|
"""Find the sender in the visible chat and click to open their profile card."""
|
||||||
# Try multiple selectors that contain the sender's name.
|
|
||||||
selectors = [
|
|
||||||
f"[data-tid='message-author-name']:has-text('{display_name}')",
|
|
||||||
f"[data-tid='reply-message-header'] span:has-text('{display_name}')",
|
|
||||||
f"span:has-text('{display_name}')",
|
|
||||||
]
|
|
||||||
|
|
||||||
clicked = False
|
# Strategy 1: find reply-message-header containing the name, click its avatar
|
||||||
for selector in selectors:
|
for header in self._page.query_selector_all("[data-tid='reply-message-header']"):
|
||||||
try:
|
try:
|
||||||
loc = self._page.locator(selector).first
|
name_spans = header.query_selector_all("span")
|
||||||
if loc.is_visible(timeout=1000):
|
for span in name_spans:
|
||||||
loc.click()
|
if span.inner_text().strip() == display_name:
|
||||||
clicked = True
|
avatar = header.query_selector(
|
||||||
break
|
"[data-tid='reply-message-header-avatar']"
|
||||||
|
)
|
||||||
|
target = avatar or span
|
||||||
|
target.scroll_into_view_if_needed()
|
||||||
|
target.click()
|
||||||
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Strategy 2: message-author-name (private meeting chat)
|
||||||
|
for el in self._page.query_selector_all("[data-tid='message-author-name']"):
|
||||||
|
try:
|
||||||
|
if el.inner_text().strip() == display_name:
|
||||||
|
el.scroll_into_view_if_needed()
|
||||||
|
el.click()
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _extract_email_from_profile(self, display_name: str) -> str | None:
|
||||||
|
try:
|
||||||
|
clicked = self._find_and_click_sender(display_name)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Klick auf '{display_name}' fehlgeschlagen: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
if not clicked:
|
if not clicked:
|
||||||
print(f" Sender '{display_name}' nicht im Chat gefunden.")
|
print(f" '{display_name}' nicht im sichtbaren Chat gefunden.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Step 1: wait for the profile card container to appear
|
# Wait for profile card container
|
||||||
self._page.wait_for_selector(".lpc_ip_root_class", timeout=5000)
|
self._page.wait_for_selector(".lpc_ip_root_class", timeout=5000)
|
||||||
except Exception:
|
except Exception:
|
||||||
print(f" Profilkarte für '{display_name}' öffnet sich nicht (Klick hat nicht gewirkt).")
|
print(f" Profilkarte für '{display_name}' öffnet sich nicht.")
|
||||||
self._page.keyboard.press("Escape")
|
self._page.keyboard.press("Escape")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Step 2: extract email — try mailto link first, then plain text fallbacks
|
|
||||||
email = None
|
email = None
|
||||||
for selector in [
|
for selector in [
|
||||||
".lpc_ip_root_class a[href*='mailto:']",
|
".lpc_ip_root_class a[href*='mailto:']",
|
||||||
@ -86,14 +112,14 @@ class Resolver:
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not email:
|
if not email:
|
||||||
# Diagnose: show card text content
|
|
||||||
card = self._page.query_selector(".lpc_ip_root_class")
|
card = self._page.query_selector(".lpc_ip_root_class")
|
||||||
card_text = card.inner_text().strip()[:200] if card else "(leer)"
|
card_text = card.inner_text().strip()[:200] if card else "(leer)"
|
||||||
print(f" Karte geöffnet, E-Mail nicht gefunden. Karteninhalt: {card_text!r}")
|
print(f" Karte geöffnet, E-Mail nicht gefunden. Inhalt: {card_text!r}")
|
||||||
|
|
||||||
self._page.keyboard.press("Escape")
|
self._page.keyboard.press("Escape")
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
return email
|
return email
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" Fehler beim Lesen der Profilkarte: {e}")
|
print(f" Fehler beim Lesen der Profilkarte: {e}")
|
||||||
self._page.keyboard.press("Escape")
|
self._page.keyboard.press("Escape")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user