Improve display name targeting with exact/partial match and recency preference

This commit is contained in:
2025-10-03 13:30:41 -07:00
parent 24f7f3e265
commit 570b15c5a4

66
main.py
View File

@@ -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