Merge pull request #57 from artgas1/feature/add-contact-username-support

feat: Add support for adding contacts by username without phone number
This commit is contained in:
Eugene Evstafev 2026-02-12 12:23:04 +00:00 committed by GitHub
commit 95a0313b5c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

141
main.py
View file

@ -1300,59 +1300,132 @@ async def get_message_context(
title="Add Contact", openWorldHint=True, destructiveHint=True, idempotentHint=True title="Add Contact", openWorldHint=True, destructiveHint=True, idempotentHint=True
) )
) )
async def add_contact(phone: str, first_name: str, last_name: str = "") -> str: async def add_contact(
phone: Optional[str] = None,
first_name: str = "",
last_name: str = "",
username: Optional[str] = None,
) -> str:
""" """
Add a new contact to your Telegram account. Add a new contact to your Telegram account.
Args: Args:
phone: The phone number of the contact (with country code). phone: The phone number of the contact (with country code). Required if username is not provided.
first_name: The contact's first name. first_name: The contact's first name.
last_name: The contact's last name (optional). last_name: The contact's last name (optional).
username: The Telegram username (without @). Use this for adding contacts without phone numbers.
Note: Either phone or username must be provided. If username is provided, the function will resolve it
and add the contact using contacts.addContact API (which supports adding contacts without phone numbers).
""" """
try: try:
# Try to import the required types first # Normalize None to empty string for easier checking
from telethon.tl.types import InputPhoneContact phone = phone or ""
username = username or ""
result = await client( # Validate that at least one identifier is provided
functions.contacts.ImportContactsRequest( if not phone and not username:
contacts=[ return "Error: Either phone or username must be provided."
InputPhoneContact(
client_id=0, # If username is provided, use it for username-based contact addition
phone=phone, if username:
# Remove @ if present
username_clean = username.lstrip("@")
if not username_clean:
return "Error: Username cannot be empty."
# Resolve username to get user information
try:
resolve_result = await client(
functions.contacts.ResolveUsernameRequest(username=username_clean)
)
# Extract user from the result
if not resolve_result.users:
return f"Error: User with username @{username_clean} not found."
user = resolve_result.users[0]
if not isinstance(user, User):
return f"Error: Resolved entity is not a user."
user_id = user.id
access_hash = user.access_hash
# Use contacts.addContact to add the contact by user ID
from telethon.tl.types import InputUser
result = await client(
functions.contacts.AddContactRequest(
id=InputUser(user_id=user_id, access_hash=access_hash),
first_name=first_name, first_name=first_name,
last_name=last_name, last_name=last_name,
phone="", # Empty phone for username-based contacts
) )
] )
)
) if hasattr(result, "updates") and result.updates:
if result.imported: return (
return f"Contact {first_name} {last_name} added successfully." f"Contact {first_name} {last_name} (@{username_clean}) added successfully."
else: )
return f"Contact not added. Response: {str(result)}" else:
except (ImportError, AttributeError) as type_err: return f"Contact {first_name} {last_name} (@{username_clean}) added successfully (no updates returned)."
# Try alternative approach using raw API
try: except Exception as resolve_e:
logger.exception(
f"add_contact (username resolve) failed (username={username_clean})"
)
return log_and_format_error("add_contact", resolve_e, username=username_clean)
elif phone:
# Original phone-based contact addition
from telethon.tl.types import InputPhoneContact
result = await client( result = await client(
functions.contacts.ImportContactsRequest( functions.contacts.ImportContactsRequest(
contacts=[ contacts=[
{ InputPhoneContact(
"client_id": 0, client_id=0,
"phone": phone, phone=phone,
"first_name": first_name, first_name=first_name,
"last_name": last_name, last_name=last_name,
} )
] ]
) )
) )
if hasattr(result, "imported") and result.imported: if result.imported:
return f"Contact {first_name} {last_name} added successfully (alt method)." return f"Contact {first_name} {last_name} added successfully."
else: else:
return f"Contact not added. Alternative method response: {str(result)}" return f"Contact not added. Response: {str(result)}"
except Exception as alt_e: else:
logger.exception(f"add_contact (alt method) failed (phone={phone})") return "Error: Phone number is required when username is not provided."
return log_and_format_error("add_contact", alt_e, phone=phone) except (ImportError, AttributeError) as type_err:
# Try alternative approach using raw API (only for phone-based)
if phone and not username:
try:
result = await client(
functions.contacts.ImportContactsRequest(
contacts=[
{
"client_id": 0,
"phone": phone,
"first_name": first_name,
"last_name": last_name,
}
]
)
)
if hasattr(result, "imported") and result.imported:
return f"Contact {first_name} {last_name} added successfully (alt method)."
else:
return f"Contact not added. Alternative method response: {str(result)}"
except Exception as alt_e:
logger.exception(f"add_contact (alt method) failed (phone={phone})")
return log_and_format_error("add_contact", alt_e, phone=phone)
else:
logger.exception(f"add_contact (type error) failed")
return log_and_format_error("add_contact", type_err)
except Exception as e: except Exception as e:
logger.exception(f"add_contact failed (phone={phone})") logger.exception(f"add_contact failed (phone={phone}, username={username})")
return log_and_format_error("add_contact", e, phone=phone) return log_and_format_error("add_contact", e, phone=phone, username=username)
@mcp.tool( @mcp.tool(