forked from CameronJGrant/telegram-scam-baiter
Improve display name targeting with exact/partial match and recency preference
This commit is contained in:
66
main.py
66
main.py
@@ -121,6 +121,24 @@ def last_history_role() -> str | None:
|
||||
rec = last_history_record()
|
||||
return rec.get("role") if rec else None
|
||||
|
||||
# ---------- Name matching helpers ----------
|
||||
def _norm(s: str) -> str:
|
||||
return " ".join((s or "").split()).strip().lower()
|
||||
|
||||
def _display_name_from_entity(ent) -> str:
|
||||
# Telethon dialog .name is already a nice display for users; fallback to first+last
|
||||
name = getattr(ent, "first_name", None)
|
||||
last = getattr(ent, "last_name", None)
|
||||
if name or last:
|
||||
return " ".join([n for n in [name, last] if n])
|
||||
# Fallback to generic 'name' attribute if present
|
||||
return getattr(ent, "name", "") or ""
|
||||
|
||||
def _name_matches(display_name_env: str, entity) -> bool:
|
||||
target = _norm(display_name_env)
|
||||
actual = _norm(_display_name_from_entity(entity))
|
||||
return actual == target or (target and target in actual)
|
||||
# ... existing code ...
|
||||
|
||||
# ---------- OpenAI client ----------
|
||||
oai = OpenAI(api_key=OPENAI_API_KEY)
|
||||
@@ -178,17 +196,49 @@ async def resolve_target_entity(client: TelegramClient):
|
||||
except Exception:
|
||||
target = None
|
||||
|
||||
# Try resolving by display name across dialogs (case-insensitive exact match)
|
||||
# Resolve by the display name they set for themselves (case-insensitive).
|
||||
# Prefer exact match; if multiple, pick the most recent dialog.
|
||||
if not target and TARGET_DISPLAY_NAME:
|
||||
best_candidate = None
|
||||
best_exact = False
|
||||
best_date = None
|
||||
try:
|
||||
async for d in client.iter_dialogs():
|
||||
ent = d.entity
|
||||
name = getattr(d, "name", "") or ""
|
||||
if name.strip().lower() == TARGET_DISPLAY_NAME.lower():
|
||||
# Make sure it's a user dialog, not a group
|
||||
if hasattr(ent, "bot") or getattr(ent, "is_self", False):
|
||||
pass
|
||||
return ent
|
||||
# Only consider real users (not bots, channels, groups, or yourself)
|
||||
if not isinstance(ent, User):
|
||||
continue
|
||||
if getattr(ent, "bot", False) or getattr(ent, "is_self", False):
|
||||
continue
|
||||
|
||||
actual_name = _display_name_from_entity(ent)
|
||||
if not actual_name:
|
||||
continue
|
||||
|
||||
actual_norm = _norm(actual_name)
|
||||
target_norm = _norm(TARGET_DISPLAY_NAME)
|
||||
if not target_norm:
|
||||
continue
|
||||
|
||||
is_exact = actual_norm == target_norm
|
||||
is_partial = (target_norm in actual_norm)
|
||||
|
||||
if not (is_exact or is_partial):
|
||||
continue
|
||||
|
||||
this_date = getattr(d.message, "date", None)
|
||||
if best_candidate is None:
|
||||
best_candidate, best_exact, best_date = ent, is_exact, this_date
|
||||
continue
|
||||
|
||||
# Prefer exact matches over partial; then most recent
|
||||
if is_exact and not best_exact:
|
||||
best_candidate, best_exact, best_date = ent, True, this_date
|
||||
elif (is_exact == best_exact) and (this_date and (not best_date or this_date > best_date)):
|
||||
best_candidate, best_exact, best_date = ent, is_exact, this_date
|
||||
|
||||
if best_candidate:
|
||||
return best_candidate
|
||||
except Exception:
|
||||
target = None
|
||||
|
||||
@@ -286,7 +336,7 @@ async def main():
|
||||
if TARGET_USER_ID:
|
||||
print(f"Hint: couldn't resolve by user id '{TARGET_USER_ID}'.")
|
||||
if TARGET_DISPLAY_NAME:
|
||||
print(f"Hint: couldn't resolve by display name '{TARGET_DISPLAY_NAME}'.")
|
||||
print(f"Hint: couldn't resolve by display name '{TARGET_DISPLAY_NAME}'. The dialog may not exist yet.")
|
||||
|
||||
# If we already have a target, attempt a startup catch-up reply if their message was last.
|
||||
catchup_sent = False
|
||||
|
||||
Reference in New Issue
Block a user