├── tests ├── __init__.py └── test_truecallerpy.py ├── src └── truecallerpy │ ├── data │ ├── __init__.py │ └── phones_list.py │ ├── __init__.py │ ├── verify_otp.py │ ├── login.py │ ├── search.py │ ├── typings │ └── truecallerpy.pyi │ └── cli.py ├── .gitignore ├── requirements.txt ├── .github └── FUNDING.yml ├── LICENSE ├── pyproject.toml └── README.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/truecallerpy/data/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/truecallerpy/__init__.py: -------------------------------------------------------------------------------- 1 | from .login import login 2 | from .verify_otp import verify_otp 3 | from .search import search_phonenumber,bulk_search -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | truecallerpy-venv 2 | build 3 | .pytest_cache 4 | tests/__pycache__ 5 | src/truecallerpy/__pycache__ 6 | src/truecallerpy.egg-info 7 | src/truecallerpy/data/__pycache__ 8 | dist -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | anyio==3.7.1 2 | certifi==2023.7.22 3 | colorama==0.4.6 4 | h11==0.14.0 5 | httpcore==0.17.3 6 | httpx==0.24.1 7 | idna==3.4 8 | iniconfig==2.0.0 9 | packaging==23.1 10 | phonenumbers==8.13.18 11 | pluggy==1.2.0 12 | prompt-toolkit==3.0.36 13 | pytest==7.4.0 14 | questionary==2.0.0 15 | sniffio==1.3.0 16 | wcwidth==0.2.6 17 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: sumithemmadi 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | # liberapay: sumithemmadi 10 | # issuehunt: sumithemmadi 11 | otechie: # Replace with a single Otechie username 12 | custom: ["https://paypal.me/sumithemmadi"] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Emmadi Sumith Kumar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /tests/test_truecallerpy.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from truecallerpy import login, verify_otp, search_phonenumber, bulk_search 3 | import asyncio 4 | 5 | # Test login function 6 | 7 | 8 | def test_login(): 9 | phone_number = "+919912345654" 10 | response = asyncio.run(login(phone_number)) 11 | assert "data" in response["data"] 12 | 13 | # Test verify_otp function 14 | 15 | 16 | def test_verify_otp(): 17 | phone_number = "+919912345674" 18 | response = asyncio.run(login(phone_number)) 19 | otp = "123456" 20 | response = asyncio.run(verify_otp(phone_number, response["data"], otp)) 21 | assert "message" in response["data"] 22 | 23 | # # Test search function 24 | 25 | 26 | def test_search(): 27 | phone_number = "+1234567890" 28 | country_code = "US" 29 | installation_id = "a1i0G--h" 30 | response = asyncio.run(search_phonenumber( 31 | phone_number, country_code, installation_id)) 32 | assert "data" in response 33 | 34 | # # Test bulk_search function 35 | 36 | 37 | def test_bulk_search(): 38 | phone_numbers = "+1234567890,+9876543210" 39 | country_code = "US" 40 | installation_id = "a1i0G--h" 41 | response = asyncio.run(bulk_search( 42 | phone_numbers, country_code, installation_id)) 43 | assert "data" in response["data"] 44 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=61.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "truecallerpy" 7 | version = "1.0.3" 8 | description = "TruecallerPy is a Python package that provides functionalities to interact with the Truecaller API. It allows you to perform login, OTP verification, and phone number search using Truecaller." 9 | readme = "README.md" 10 | license = { file = "LICENSE" } 11 | keywords = [ 12 | "truecallerpy", 13 | "truecaller", 14 | "search", 15 | "phone", 16 | "number", 17 | "phonenumber", 18 | "information", 19 | "mobile", 20 | ] 21 | authors = [{ name = "Sumith Emmadi", email = "sumithemmadi244@gmail.com" }] 22 | maintainers = [{ name = "Sumith Emmadi", email = "sumithemmadi244@gmail.com" }] 23 | 24 | classifiers = [ 25 | "Topic :: Utilities", 26 | "Natural Language :: English", 27 | "Operating System :: OS Independent", 28 | "Programming Language :: Python :: 3", 29 | "License :: OSI Approved :: MIT License", 30 | ] 31 | 32 | dependencies = ["httpx", "phonenumbers", "questionary", "colorama"] 33 | 34 | [project.urls] 35 | Homepage = "https://github.com/sumithemmadi/truecallerpy" 36 | Documentation = "https://github.com/sumithemmadi/truecallerpy" 37 | Repository = "https://github.com/sumithemmadi/truecallerpy" 38 | Changelog = "https://github.com/sumithemmadi/truecallerpy" 39 | "Bug Tracker" = "https://github.com/sumithemmadi/truecallerpy/issues" 40 | 41 | [project.scripts] 42 | truecallerpy = "truecallerpy.cli:main" 43 | -------------------------------------------------------------------------------- /src/truecallerpy/verify_otp.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import phonenumbers 3 | from phonenumbers import region_code_for_country_code 4 | 5 | 6 | async def verify_otp(phone_number, json_data, otp): 7 | """ 8 | Verify the OTP (One-Time Password) for phone number verification. 9 | 10 | Args: 11 | phone_number (str): The phone number in international format. 12 | json_data (dict): The JSON response data from the login request containing the requestId. 13 | otp (str): The OTP to verify. 14 | 15 | Returns: 16 | dict: The verification response containing the result of the OTP verification. 17 | 18 | Raises: 19 | ValueError: If the phone number is invalid. 20 | httpx.RequestError: If an error occurs during the API request. 21 | """ 22 | try: 23 | parsed_number = phonenumbers.parse(phone_number) 24 | if not phonenumbers.is_valid_number(parsed_number): 25 | raise ValueError("Phone number should be in international format.") 26 | 27 | country_code = str(region_code_for_country_code( 28 | parsed_number.country_code)) 29 | dialing_code = parsed_number.country_code 30 | phone_number = str(parsed_number.national_number) 31 | 32 | post_data = { 33 | "countryCode": country_code, 34 | "dialingCode": dialing_code, 35 | "phoneNumber": phone_number, 36 | "requestId": json_data["requestId"], 37 | "token": otp, 38 | } 39 | 40 | headers = { 41 | "content-type": "application/json; charset=UTF-8", 42 | "accept-encoding": "gzip", 43 | "user-agent": "Truecaller/11.75.5 (Android;10)", 44 | "clientsecret": "lvc22mp3l1sfv6ujg83rd17btt", 45 | } 46 | 47 | url = "https://account-asia-south1.truecaller.com/v1/verifyOnboardingOtp" 48 | 49 | async with httpx.AsyncClient() as client: 50 | response = await client.post(url, json=post_data, headers=headers) 51 | 52 | response.raise_for_status() 53 | 54 | return { 55 | "status_code": response.status_code, 56 | "data": response.json() 57 | } 58 | 59 | except httpx.HTTPError as exc: 60 | error_message = "An HTTP error occurred: " + str(exc) 61 | return { 62 | "status_code": exc.response.status_code if hasattr(exc, "response") else None, 63 | "error": "HTTP Error", 64 | "message": error_message 65 | } -------------------------------------------------------------------------------- /src/truecallerpy/login.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | import random 3 | from phonenumbers import parse as parse_phone_number 4 | from phonenumbers.phonenumberutil import region_code_for_country_code 5 | from .data.phones_list import get_random_device 6 | 7 | 8 | async def generate_random_string(length: int) -> str: 9 | """ 10 | Generate a random string of the given length. 11 | 12 | Args: 13 | length (int): The length of the random string. 14 | 15 | Returns: 16 | str: The generated random string. 17 | """ 18 | characters = "abcdefghijklmnopqrstuvwxyz0123456789" 19 | return ''.join(random.choice(characters) for _ in range(length)) 20 | 21 | 22 | async def login(phone_number: str) -> dict: 23 | """ 24 | Login to Truecaller. 25 | 26 | Args: 27 | phone_number (str): Phone number in international format. 28 | 29 | Returns: 30 | dict: The login response containing the requestId used for OTP verification. 31 | """ 32 | pn = parse_phone_number(phone_number, None) 33 | device = get_random_device() 34 | 35 | if not pn or not pn.country_code or not pn.national_number: 36 | raise ValueError("Invalid phone number.") 37 | 38 | post_url = "https://account-asia-south1.truecaller.com/v2/sendOnboardingOtp" 39 | 40 | data = { 41 | "countryCode": str(region_code_for_country_code(pn.country_code)), 42 | "dialingCode": pn.country_code, 43 | "installationDetails": { 44 | "app": { 45 | "buildVersion": 5, 46 | "majorVersion": 11, 47 | "minorVersion": 7, 48 | "store": "GOOGLE_PLAY", 49 | }, 50 | "device": { 51 | "deviceId": await generate_random_string(16), 52 | "language": "en", 53 | "manufacturer": device["manufacturer"], 54 | "model": device["model"], 55 | "osName": "Android", 56 | "osVersion": "10", 57 | "mobileServices": ["GMS"], 58 | }, 59 | "language": "en", 60 | }, 61 | "phoneNumber": str(pn.national_number), 62 | "region": "region-2", 63 | "sequenceNo": 2, 64 | } 65 | 66 | headers = { 67 | "content-type": "application/json; charset=UTF-8", 68 | "accept-encoding": "gzip", 69 | "user-agent": "Truecaller/11.75.5 (Android;10)", 70 | "clientsecret": "lvc22mp3l1sfv6ujg83rd17btt", 71 | } 72 | try: 73 | async with httpx.AsyncClient() as client: 74 | response = await client.post(post_url, json=data, headers=headers) 75 | 76 | response.raise_for_status() 77 | 78 | return { 79 | "status_code": response.status_code, 80 | "data": response.json() 81 | } 82 | except httpx.HTTPError as exc: 83 | error_message = "An HTTP error occurred: " + str(exc) 84 | return { 85 | "status_code": exc.response.status_code if hasattr(exc, "response") else None, 86 | "error": "HTTP Error", 87 | "message": error_message 88 | } 89 | -------------------------------------------------------------------------------- /src/truecallerpy/search.py: -------------------------------------------------------------------------------- 1 | import httpx 2 | from phonenumbers import parse 3 | 4 | 5 | async def search_phonenumber(phoneNumber, countryCode, installationId): 6 | """ 7 | Search for a phone number using Truecaller API. 8 | 9 | Args: 10 | phoneNumber (str): The phone number to search. 11 | countryCode (str): The country code of the phone number. 12 | installationId (str): The installation ID for authorization. 13 | 14 | Returns: 15 | dict: The search result containing information about the phone number. 16 | 17 | Raises: 18 | httpx.RequestError: If an error occurs during the API request. 19 | """ 20 | phone_number = parse(str(phoneNumber), str(countryCode)) 21 | significant_number = phone_number.national_number 22 | 23 | headers = { 24 | "content-type": "application/json; charset=UTF-8", 25 | "accept-encoding": "gzip", 26 | "user-agent": "Truecaller/11.75.5 (Android;10)", 27 | "Authorization": f"Bearer {installationId}" 28 | } 29 | params = { 30 | "q": str(significant_number), 31 | "countryCode": phone_number.country_code, 32 | "type": 4, 33 | "locAddr": "", 34 | "placement": "SEARCHRESULTS,HISTORY,DETAILS", 35 | "encoding": "json" 36 | } 37 | 38 | try: 39 | async with httpx.AsyncClient() as client: 40 | response = await client.get( 41 | "https://search5-noneu.truecaller.com/v2/search", params=params, headers=headers 42 | ) 43 | 44 | response.raise_for_status() 45 | 46 | return { 47 | "status_code": response.status_code, 48 | "data": response.json() 49 | } 50 | except httpx.HTTPError as exc: 51 | error_message = "An HTTP error occurred: " + str(exc) 52 | return { 53 | "status_code": exc.response.status_code if hasattr(exc, "response") else None, 54 | "error": "HTTP Error", 55 | "message": error_message 56 | } 57 | 58 | 59 | 60 | async def bulk_search(phoneNumbers, countryCode, installationId): 61 | """ 62 | Perform bulk search for a list of phone numbers using Truecaller API. 63 | 64 | Args: 65 | phoneNumbers (list[str]): The list of phone numbers to search. 66 | countryCode (str): The country code of the phone numbers. 67 | installationId (str): The installation ID for authorization. 68 | 69 | Returns: 70 | dict: The bulk search result containing information about the phone numbers. 71 | 72 | Raises: 73 | httpx.RequestError: If an error occurs during the API request. 74 | """ 75 | headers = { 76 | "content-type": "application/json; charset=UTF-8", 77 | "accept-encoding": "gzip", 78 | "user-agent": "Truecaller/11.75.5 (Android;10)", 79 | "Authorization": f"Bearer {installationId}" 80 | } 81 | params = { 82 | "q": str(phoneNumbers), 83 | "countryCode": countryCode, 84 | "type": 14, 85 | "placement": "SEARCHRESULTS,HISTORY,DETAILS", 86 | "encoding": "json" 87 | } 88 | try: 89 | async with httpx.AsyncClient() as client: 90 | response = await client.get( 91 | "https://search5-noneu.truecaller.com/v2/bulk", params=params, headers=headers 92 | ) 93 | response.raise_for_status() 94 | 95 | return { 96 | "status_code": response.status_code, 97 | "data": response.json() 98 | } 99 | except httpx.HTTPError as exc: 100 | error_message = "An HTTP error occurred: " + str(exc) 101 | return { 102 | "status_code": exc.response.status_code if hasattr(exc, "response") else None, 103 | "error": "HTTP Error", 104 | "message": error_message 105 | } 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/truecallerpy/typings/truecallerpy.pyi: -------------------------------------------------------------------------------- 1 | import random 2 | import requests 3 | from typing import Dict, List, Union 4 | import phonenumbers 5 | from phonenumbers import parse as parse_phone_number 6 | from phonenumbers.phonenumberutil import region_code_for_country_code 7 | from ..data.phones_list import get_random_device 8 | 9 | def generate_random_string(length: int) -> str: 10 | """ 11 | Generate a random string of the given length. 12 | 13 | Args: 14 | length (int): The length of the random string. 15 | 16 | Returns: 17 | str: The generated random string. 18 | """ 19 | characters = "abcdefghijklmnopqrstuvwxyz0123456789" 20 | return ''.join(random.choice(characters) for _ in range(length)) 21 | 22 | 23 | def login(phone_number: str) -> Dict[str, Union[str, int]]: 24 | """ 25 | Login to Truecaller. 26 | 27 | Args: 28 | phone_number (str): Phone number in international format. 29 | 30 | Returns: 31 | dict: The login response containing the requestId used for OTP verification. 32 | 33 | Raises: 34 | ValueError: If the phone number is invalid. 35 | requests.exceptions.RequestException: If an error occurs during the API request. 36 | """ 37 | pn = parse_phone_number(phone_number, None) 38 | device = get_random_device() 39 | 40 | if not pn or not pn.country_code or not pn.national_number: 41 | raise ValueError("Invalid phone number.") 42 | 43 | post_url = "https://account-asia-south1.truecaller.com/v2/sendOnboardingOtp" 44 | 45 | data = { 46 | "countryCode": str(region_code_for_country_code(pn.country_code)), 47 | "dialingCode": pn.country_code, 48 | "installationDetails": { 49 | "app": { 50 | "buildVersion": 5, 51 | "majorVersion": 11, 52 | "minorVersion": 7, 53 | "store": "GOOGLE_PLAY", 54 | }, 55 | "device": { 56 | "deviceId": generate_random_string(16), 57 | "language": "en", 58 | "manufacturer": device["manufacturer"], 59 | "model": device["model"], 60 | "osName": "Android", 61 | "osVersion": "10", 62 | "mobileServices": ["GMS"], 63 | }, 64 | "language": "en", 65 | }, 66 | "phoneNumber": str(pn.national_number), 67 | "region": "region-2", 68 | "sequenceNo": 2, 69 | } 70 | 71 | headers = { 72 | "content-type": "application/json; charset=UTF-8", 73 | "accept-encoding": "gzip", 74 | "user-agent": "Truecaller/11.75.5 (Android;10)", 75 | "clientsecret": "lvc22mp3l1sfv6ujg83rd17btt", 76 | } 77 | 78 | response = requests.post(post_url, json=data, headers=headers) 79 | return response.json() 80 | 81 | 82 | def verify_otp(phone_number: str, json_data: Dict[str, str], otp: str) -> Dict[str, Union[str, int]]: 83 | """ 84 | Verify the OTP (One-Time Password) for phone number verification. 85 | 86 | Args: 87 | phone_number (str): The phone number in international format. 88 | json_data (dict): The JSON response data from the login request containing the requestId. 89 | otp (str): The OTP to verify. 90 | 91 | Returns: 92 | dict: The verification response containing the result of the OTP verification. 93 | 94 | Raises: 95 | ValueError: If the phone number is invalid. 96 | requests.exceptions.RequestException: If an error occurs during the API request. 97 | """ 98 | try: 99 | parsed_number = parse_phone_number(phone_number) 100 | if not phonenumbers.is_valid_number(parsed_number): 101 | raise ValueError("Phone number should be in international format.") 102 | 103 | country_code = str(region_code_for_country_code( 104 | parsed_number.country_code)) 105 | dialing_code = parsed_number.country_code 106 | phone_number = str(parsed_number.national_number) 107 | 108 | post_data = { 109 | "countryCode": country_code, 110 | "dialingCode": dialing_code, 111 | "phoneNumber": phone_number, 112 | "requestId": json_data["requestId"], 113 | "token": otp, 114 | } 115 | 116 | headers = { 117 | "content-type": "application/json; charset=UTF-8", 118 | "accept-encoding": "gzip", 119 | "user-agent": "Truecaller/11.75.5 (Android;10)", 120 | "clientsecret": "lvc22mp3l1sfv6ujg83rd17btt", 121 | } 122 | 123 | url = "https://account-asia-south1.truecaller.com/v1/verifyOnboardingOtp" 124 | 125 | response = requests.post(url, json=post_data, headers=headers) 126 | return response.json() 127 | 128 | except phonenumbers.phonenumberutil.NumberParseException: 129 | raise ValueError("Invalid phone number.") 130 | 131 | 132 | def search(phone_number: str, country_code: str, installation_id: str) -> Dict[str, any]: 133 | """ 134 | Search for a phone number using Truecaller API. 135 | 136 | Args: 137 | phone_number (str): The phone number to search. 138 | country_code (str): The country code of the phone number. 139 | installation_id (str): The installation ID for authorization. 140 | 141 | Returns: 142 | dict: The search result containing information about the phone number. 143 | 144 | Raises: 145 | requests.exceptions.RequestException: If an error occurs during the API request. 146 | """ 147 | phone_number = parse_phone_number(phone_number, country_code) 148 | significant_number = phone_number.national_number 149 | 150 | headers = { 151 | "content-type": "application/json; charset=UTF-8", 152 | "accept-encoding": "gzip", 153 | "user-agent": "Truecaller/11.75.5 (Android;10)", 154 | "Authorization": f"Bearer {installation_id}" 155 | } 156 | params = { 157 | "q": str(significant_number), 158 | "countryCode": phone_number.country_code, 159 | "type": 4, 160 | "locAddr": "", 161 | "placement": "SEARCHRESULTS,HISTORY,DETAILS", 162 | "encoding": "json" 163 | } 164 | response = requests.get( 165 | "https://search5-noneu.truecaller.com/v2/search", params=params, headers=headers) 166 | 167 | response_data = response.json() 168 | return response_data 169 | 170 | 171 | def bulk_search(phone_numbers: List[str], country_code: str, installation_id: str) -> Dict[str, any]: 172 | """ 173 | Perform bulk search for a list of phone numbers using Truecaller API. 174 | 175 | Args: 176 | phone_numbers (List[str]): The list of phone numbers to search. 177 | country_code (str): The country code of the phone numbers. 178 | installation_id (str): The installation ID for authorization. 179 | 180 | Returns: 181 | dict: The bulk search result containing information about the phone numbers. 182 | 183 | Raises: 184 | requests.exceptions.RequestException: If an error occurs during the API request. 185 | """ 186 | headers = { 187 | "content-type": "application/json; charset=UTF-8", 188 | "accept-encoding": "gzip", 189 | "user-agent": "Truecaller/11.75.5 (Android;10)", 190 | "Authorization": f"Bearer {installation_id}" 191 | } 192 | params = { 193 | "q": phone_numbers, 194 | "countryCode": country_code, 195 | "type": 14, 196 | "placement": "SEARCHRESULTS,HISTORY,DETAILS", 197 | "encoding": "json" 198 | } 199 | response = requests.get( 200 | "https://search5-noneu.truecaller.com/v2/bulk", params=params, headers=headers) 201 | 202 | response_data = response.json() 203 | return response_data 204 | -------------------------------------------------------------------------------- /src/truecallerpy/data/phones_list.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | phones_list = [ 4 | { 5 | "manufacturer": "Xiaomi", 6 | "model": "M2010J19SG", 7 | }, 8 | { 9 | "manufacturer": "Xiaomi", 10 | "model": "POCO F1", 11 | }, 12 | { 13 | "manufacturer": "Xiaomi", 14 | "model": "Redmi 9A", 15 | }, 16 | { 17 | "manufacturer": "Xiaomi", 18 | "model": "Xiaomi Mi 4", 19 | }, 20 | { 21 | "manufacturer": "Xiaomi", 22 | "model": "Redmi Note 10 pro", 23 | }, 24 | { 25 | "manufacturer": "Xiaomi", 26 | "model": "Redmi Note 10", 27 | }, 28 | { 29 | "manufacturer": "Xiaomi", 30 | "model": "Xiaomi Redmi 1S", 31 | }, 32 | { 33 | "manufacturer": "Xiaomi", 34 | "model": "Xiaomi Mi 10T", 35 | }, 36 | { 37 | "manufacturer": "Xiaomi", 38 | "model": "Xiaomi Redmi 6 Pro", 39 | }, 40 | { 41 | "manufacturer": "Xiaomi", 42 | "model": "Xiaomi Redmi Y3", 43 | }, 44 | { 45 | "manufacturer": "Xiaomi", 46 | "model": "Xiaomi Redmi 9 Prime", 47 | }, 48 | { 49 | "manufacturer": "Xiaomi", 50 | "model": "Redmi Note 7", 51 | }, 52 | { 53 | "manufacturer": "Vivo", 54 | "model": "Vivo Y33s", 55 | }, 56 | { 57 | "manufacturer": "Vivo", 58 | "model": "Vivo V21 5G", 59 | }, 60 | { 61 | "manufacturer": "Vivo", 62 | "model": "Vivo Y20T", 63 | }, 64 | { 65 | "manufacturer": "Vivo", 66 | "model": "Vivo Y73 2021", 67 | }, 68 | { 69 | "manufacturer": "Vivo", 70 | "model": "Vivo X60", 71 | }, 72 | { 73 | "manufacturer": "Vivo", 74 | "model": "Vivo X70 Pro 5G", 75 | }, 76 | { 77 | "manufacturer": "Vivo", 78 | "model": "Vivo U3x", 79 | }, 80 | { 81 | "manufacturer": "Vivo", 82 | "model": "Vivo V20 Pro", 83 | }, 84 | { 85 | "manufacturer": "Vivo", 86 | "model": "Vivo Y21 2021", 87 | }, 88 | { 89 | "manufacturer": "Vivo", 90 | "model": "Vivo Y53s", 91 | }, 92 | { 93 | "manufacturer": "Vivo", 94 | "model": "Vivo S12 Pro", 95 | }, 96 | { 97 | "manufacturer": "Vivo", 98 | "model": "Vivo V21e 5G", 99 | }, 100 | { 101 | "manufacturer": "OnePlus", 102 | "model": "OnePlus Nord CE 5G", 103 | }, 104 | { 105 | "manufacturer": "OnePlus", 106 | "model": "OnePlus 9 Pro", 107 | }, 108 | { 109 | "manufacturer": "OnePlus", 110 | "model": "OnePlus 8T", 111 | }, 112 | { 113 | "manufacturer": "OnePlus", 114 | "model": "OnePlus 9", 115 | }, 116 | { 117 | "manufacturer": "OnePlus", 118 | "model": "OnePlus 7T", 119 | }, 120 | { 121 | "manufacturer": "OnePlus", 122 | "model": "OnePlus 6T", 123 | }, 124 | { 125 | "manufacturer": "OnePlus", 126 | "model": "OnePlus Nord 2", 127 | }, 128 | { 129 | "manufacturer": "OnePlus", 130 | "model": "OnePlus 7 Pro", 131 | }, 132 | { 133 | "manufacturer": "OnePlus", 134 | "model": "OnePlus Nord", 135 | }, 136 | { 137 | "manufacturer": "Realme", 138 | "model": "RMX2185", 139 | }, 140 | { 141 | "manufacturer": "Realme", 142 | "model": "Realme GT Neo2 5G", 143 | }, 144 | { 145 | "manufacturer": "Realme", 146 | "model": "Realme 8 5G", 147 | }, 148 | { 149 | "manufacturer": "Realme", 150 | "model": "Realme C11 2021", 151 | }, 152 | { 153 | "manufacturer": "Realme", 154 | "model": "Realme GT", 155 | }, 156 | { 157 | "manufacturer": "Realme", 158 | "model": "Realme Narzo 30", 159 | }, 160 | { 161 | "manufacturer": "Realme", 162 | "model": "Realme Q3i 5G", 163 | }, 164 | { 165 | "manufacturer": "Realme", 166 | "model": "Realme 8s 5G", 167 | }, 168 | { 169 | "manufacturer": "Realme", 170 | "model": "Realme 8i", 171 | }, 172 | { 173 | "manufacturer": "Realme", 174 | "model": "Realme Narzo 50A", 175 | }, 176 | { 177 | "manufacturer": "Realme", 178 | "model": "Realme C21Y", 179 | }, 180 | { 181 | "manufacturer": "Oppo", 182 | "model": "OPPO A55", 183 | }, 184 | { 185 | "manufacturer": "Oppo", 186 | "model": "OPPO A74 5G", 187 | }, 188 | { 189 | "manufacturer": "Oppo", 190 | "model": "OPPO A53", 191 | }, 192 | { 193 | "manufacturer": "Oppo", 194 | "model": "OPPO A31", 195 | }, 196 | { 197 | "manufacturer": "Oppo", 198 | "model": "OPPO A12", 199 | }, 200 | { 201 | "manufacturer": "Oppo", 202 | "model": "OPPO Reno6 Pro", 203 | }, 204 | { 205 | "manufacturer": "Oppo", 206 | "model": "OPPO Reno6", 207 | }, 208 | { 209 | "manufacturer": "Oppo", 210 | "model": "OPPO F19 Pro", 211 | }, 212 | { 213 | "manufacturer": "Oppo", 214 | "model": "OPPO F19s", 215 | }, 216 | { 217 | "manufacturer": "Oppo", 218 | "model": "Oppo F19 Pro+", 219 | }, 220 | { 221 | "manufacturer": "Oppo", 222 | "model": "Oppo A33", 223 | }, 224 | { 225 | "manufacturer": "Oppo", 226 | "model": "Oppo Reno 3 Pro", 227 | }, 228 | { 229 | "manufacturer": "Oppo", 230 | "model": "Oppo Reno 4 Pro", 231 | }, 232 | { 233 | "manufacturer": "Oppo", 234 | "model": "Oppo Find X2", 235 | }, 236 | { 237 | "manufacturer": "Oppo", 238 | "model": "OPPO F15", 239 | }, 240 | { 241 | "manufacturer": "Oppo", 242 | "model": "OPPO Reno 2F", 243 | }, 244 | { 245 | "manufacturer": "Oppo", 246 | "model": "OPPO K3", 247 | }, 248 | { 249 | "manufacturer": "Oppo", 250 | "model": "OPPO A9", 251 | }, 252 | { 253 | "manufacturer": "Oppo", 254 | "model": "OPPO A1k", 255 | }, 256 | { 257 | "manufacturer": "Oppo", 258 | "model": "OPPO A5s", 259 | }, 260 | { 261 | "manufacturer": "Samsung", 262 | "model": "Samsung Galaxy M31s", 263 | }, 264 | { 265 | "manufacturer": "Samsung", 266 | "model": "Samsung Galaxy M32", 267 | }, 268 | { 269 | "manufacturer": "Samsung", 270 | "model": "Samsung Galaxy F62", 271 | }, 272 | { 273 | "manufacturer": "Samsung", 274 | "model": "Samsung Galaxy M52 5G", 275 | }, 276 | { 277 | "manufacturer": "Samsung", 278 | "model": "Samsung Galaxy M12", 279 | }, 280 | { 281 | "manufacturer": "Samsung", 282 | "model": "Samsung Galaxy M51", 283 | }, 284 | { 285 | "manufacturer": "Samsung", 286 | "model": "Samsung Galaxy F12", 287 | }, 288 | { 289 | "manufacturer": "Samsung", 290 | "model": "Samsung Galaxy F22", 291 | }, 292 | { 293 | "manufacturer": "Samsung", 294 | "model": "Samsung Galaxy A52", 295 | }, 296 | { 297 | "manufacturer": "Samsung", 298 | "model": "Samsung Galaxy S20 FE 5G", 299 | }, 300 | { 301 | "manufacturer": "Samsung", 302 | "model": "Samsung Galaxy M52", 303 | }, 304 | { 305 | "manufacturer": "Samsung", 306 | "model": "Samsung Galaxy M62", 307 | }, 308 | { 309 | "manufacturer": "Samsung", 310 | "model": "Samsung Galaxy S21 Ultra", 311 | }, 312 | { 313 | "manufacturer": "Samsung", 314 | "model": "Samsung Galaxy A52s 5G", 315 | }, 316 | { 317 | "manufacturer": "Samsung", 318 | "model": "Samsung Galaxy S21", 319 | }, 320 | { 321 | "manufacturer": "Samsung", 322 | "model": "Samsung Galaxy M21 2021", 323 | }, 324 | { 325 | "manufacturer": "Samsung", 326 | "model": "Samsung Galaxy F42", 327 | }, 328 | { 329 | "manufacturer": "Samsung", 330 | "model": "Samsung Galaxy A12", 331 | }, 332 | { 333 | "manufacturer": "Samsung", 334 | "model": "Samsung Galaxy F41", 335 | }, 336 | { 337 | "manufacturer": "Samsung", 338 | "model": "Samsung Galaxy M01 Core", 339 | }, 340 | ] 341 | 342 | 343 | def get_random_device(): 344 | random_index = random.randint(0, len(phones_list) - 1) 345 | return phones_list[random_index] 346 | 347 | 348 | # device = get_random_device() 349 | 350 | # print(device) # Example usage: print the randomly selected device 351 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Truecallerpy](https://github.com/sumithemmadi/truecallerpy) 2 | 3 | [![PyPI - Implementation](https://img.shields.io/pypi/v/truecallerpy?style=flat-square)](https://pypi.org/project/truecallerpy) 4 | [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/truecallerpy?style=flat-square)](https://pypi.org/project/truecallerpy) 5 | [![Downloads](https://static.pepy.tech/badge/truecallerpy)](https://pepy.tech/project/truecallerpy) 6 | [![PyPI - License](https://img.shields.io/pypi/l/truecallerpy?style=flat-square)](https://github.com/sumithemmadi/truecallerpy/edit/main/LICENSE.md) 7 | 8 | TruecallerPy is a Python package that provides functionalities to interact with the Truecaller API. It allows you to perform login, OTP verification, and phone number search using Truecaller. 9 | 10 | **Table of Contents:** 11 | 12 | - [Truecallerpy](#truecallerpy) 13 | - [Requirements](#requirements) 14 | - [Command Line Usage](#command-line-usage) 15 | - [Installation](#installation) 16 | - [Login](#login) 17 | - [InstallationId](#installationid) 18 | - [Searching a Number](#searching-a-number) 19 | - [Bulk Phone Number Search with cli](#bulk-phone-number-search-with-cli) 20 | - [Basic Usage](#basic-usage) 21 | - [Login Function](#login-function) 22 | - [OTP Verification](#otp-verification) 23 | - [Phone Number Search](#phone-number-search) 24 | - [Bulk Phone Number Search](#bulk-phone-number-search) 25 | - [Contributing](#contributing) 26 | - [License](#license) 27 | - [Support](#support) 28 | 29 | ## Requirements 30 | 31 | To use the TruecallerPy package, you need to meet the following requirements: 32 | 33 | - Valid Mobile Number (Phone number verification for Truecaller) 34 | - Truecaller InstallationId 35 | 36 | ## Command Line Usage 37 | 38 | ### Installation 39 | 40 | You can install the TruecallerPy package using pip: 41 | 42 | ```bash 43 | pip install truecallerpy 44 | ``` 45 | 46 | ### Login 47 | 48 | To log in to your Truecaller account, use the following command: 49 | 50 | ```bash 51 | truecallerpy login 52 | ``` 53 | 54 | If you encounter any errors, try running the command with administrative privilege (e.g., `sudo truecallerpy login` on Linux or running the command prompt as administrator on Windows). 55 | 56 | ### InstallationId 57 | 58 | To retrieve your Truecaller InstallationId, use the following command: 59 | 60 | ```bash 61 | truecallerpy --installationid 62 | ``` 63 | 64 | You can also specify the `-i` flag to print only the InstallationId: 65 | 66 | ```bash 67 | truecallerpy -i -r 68 | ``` 69 | 70 | ### Searching a Number 71 | 72 | To search for a phone number using Truecaller, use the following command: 73 | 74 | ```bash 75 | truecallerpy -s [number] 76 | ``` 77 | 78 | For example: 79 | 80 | ```bash 81 | truecallerpy -s 1234567890 82 | ``` 83 | 84 | The command will return a JSON response containing information about the phone number. 85 | 86 | You can use the `-r` flag to get the raw output (JSON) instead of the formatted output. For example: 87 | 88 | ```bash 89 | truecallerpy -s 1234567890 -r 90 | ``` 91 | 92 | You can also use additional flags to extract specific information. For example, to print only the name associated with the phone number, use the `--name` flag: 93 | 94 | ```bash 95 | truecallerpy -s 1234567890 --name 96 | ``` 97 | 98 | Similarly, you can use the `--email` flag to print only the email associated with the phone number. 99 | 100 | ### Bulk Phone Number Search with cli 101 | 102 | To perform a bulk search for multiple phone numbers, use the `--bs` flag followed by the numbers separated by commas. For example: 103 | 104 | ```bash 105 | truecallerpy --bs 9912345678,+14051234567,+919987654321 106 | ``` 107 | 108 | In addition to the CLI, you can also use the TruecallerPy package in your Python code. The package provides various functions such as `login`, `verify_otp`, `search_phonenumber`, and `bulk_search`. 109 | 110 | ## Basic Usage 111 | 112 | ### Login Function 113 | 114 | The `login` function is used to log in to the Truecaller service. It takes a phone number in international format as a parameter and returns a dictionary containing the login response details. 115 | 116 | ```python 117 | import asyncio 118 | from truecallerpy import login 119 | 120 | phone_number = "+1234567890" 121 | response = asyncio.run(login(phone_number)) 122 | print(response) 123 | ``` 124 | 125 | The `login` function returns a dictionary with the following keys: 126 | 127 | - `status_code` (int): The status code of the request. 128 | - `status` (int): The status code of the truecaller login request. 129 | - `message` (str): A message indicating the status of the login request. 130 | - `domain` (str): The domain associated with the phone number. 131 | - `parsedPhoneNumber` (int): The phone number without the country code. 132 | - `parsedCountryCode` (str): The country code associated with the phone number. 133 | - `requestId` (str): The unique identifier for the login request. 134 | - `method` (str): The method used for sending the OTP. 135 | - `tokenTtl` (int): The time-to-live (TTL) value for the OTP token in seconds. 136 | 137 | ### OTP Verification 138 | 139 | The `verify_otp` function is used to verify the mobile number with the OTP (One-Time Password) received. 140 | 141 | ```python 142 | import asyncio 143 | from truecallerpy import verify_otp 144 | 145 | phone_number = "+1234567890" 146 | json_data = { 147 | # JSON response from the login function 148 | } 149 | otp = "123456" 150 | 151 | response = asyncio.run(verify_otp(phone_number, json_data, otp)) 152 | print(response) 153 | ``` 154 | 155 | The `verify_otp` function returns a dictionary with the following keys: 156 | 157 | - `status_code` (int): The status code of the request. 158 | - `status` (int): The status code of the truecaller OTP verification. 159 | - `message` (str): A message indicating the result of the OTP verification. 160 | - `installationId` (str): The installation ID associated with the verified number. 161 | - `ttl` (int): The time-to-live (TTL) value for the verification result in seconds. 162 | - `userId` (int): The user ID associated with the verified number. 163 | - `suspended` (bool): Indicates whether the account is suspended. 164 | - `phones` (list): List of phone numbers associated with the user, each containing `phoneNumber`, `countryCode`, and `priority` keys. 165 | 166 | ### Phone Number Search 167 | 168 | The `search_phonenumber` function allows you to search for a phone number using the Truecaller API. 169 | 170 | ```python 171 | import asyncio 172 | from truecallerpy import search_phonenumber 173 | 174 | phone_number = "+1234567890" 175 | country_code = "US" 176 | installation_id = "Your installation ID" 177 | 178 | response = asyncio.run(search_phonenumber(phone_number, country_code, installation_id)) 179 | print(response) 180 | ``` 181 | 182 | The `search_phonenumber` function returns a dictionary containing information about the phone number. 183 | 184 | ### Bulk Phone Number Search 185 | 186 | The `bulk_search` function allows you to perform a bulk search for a list of phone numbers using the Truecaller API. 187 | 188 | ```python 189 | import asyncio 190 | from truecallerpy import bulk_search 191 | 192 | phone_numbers = "+1234567890,9876543210" 193 | country_code = "US" 194 | installation_id = "Installation ID" 195 | 196 | response = asyncio.run(bulk_search(phone_numbers, country_code, installation_id)) 197 | print(response) 198 | ``` 199 | 200 | The `bulk_search` function returns a dictionary containing information about the phone numbers. 201 | 202 | ## Contributing 203 | 204 | Contributions to the TruecallerPy package are welcome! If you encounter any issues or have suggestions for improvements, please open an issue or submit a pull request on the project's GitHub repository. 205 | 206 | ## License 207 | 208 | This project is licensed under the MIT License. 209 | ## 💝 Sponsor and support me 210 | 211 | If you find my projects helpful or inspiring, consider supporting me through GitHub Sponsors. Your sponsorship helps me dedicate more time and effort to open source development and creating impactful projects. 212 | 213 | [:heart: Sponsor me on github](https://github.com/sponsors/sumithemmadi?o=sd&sc=t) 214 | 215 | Buy Me a Coffee at ko-fi.com 216 | 217 | sumithemmadi

218 | 219 | ### 💖 Sponsors 220 | 221 | [![Sponsors](https://sumithemmadi.github.io/sponsors.svg)](https://github.com/sponsors/sumithemmadi/) 222 | 223 | - I want to extend my sincere gratitude to all my sponsors for their generous support. 224 | 225 | ## Support 226 | 227 | If you need any assistance or have questions, please contact [sumithemmadi244@gmail.com](mailto:sumithemmadi244@gmail.com). 228 | 229 | Feel free to customize the documentation template according to your package's features and requirements. Provide detailed explanations and examples for each function, along with the necessary parameters and return types. 230 | -------------------------------------------------------------------------------- /src/truecallerpy/cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # ================================================================================== 4 | # MIT License 5 | 6 | # Copyright (c) 2022 Emmadi Sumith Kumar 7 | 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | 15 | # The above copyright notice and this permission notice shall be included in all 16 | # copies or substantial portions of the Software. 17 | 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | # ==================================================================================== 26 | 27 | import os 28 | import sys 29 | import json 30 | import argparse 31 | import phonenumbers 32 | from phonenumbers import format_number, PhoneNumberFormat 33 | import questionary 34 | import colorama 35 | from colorama import Fore, Style 36 | from .login import login 37 | from .verify_otp import verify_otp 38 | from .search import search_phonenumber, bulk_search 39 | import asyncio 40 | 41 | 42 | # Initialize colorama 43 | colorama.init() 44 | 45 | homePath = os.path.expanduser("~") 46 | truecallerpyAuthDirPath = os.path.join(homePath, ".config", "truecallerpy") 47 | requestFilePath = os.path.join(truecallerpyAuthDirPath, "request.json") 48 | authKeyFilePath = os.path.join(truecallerpyAuthDirPath, "authkey.json") 49 | 50 | if not os.path.exists(truecallerpyAuthDirPath): 51 | try: 52 | os.makedirs(truecallerpyAuthDirPath, exist_ok=True) 53 | except OSError as error: 54 | print(error) 55 | exit(1) 56 | 57 | # Function to validate phone number 58 | 59 | 60 | def validate_phone_number(input): 61 | try: 62 | pn = phonenumbers.parse(input, None) 63 | if not phonenumbers.is_valid_number(pn): 64 | return "Invalid Phone Number" 65 | return True 66 | except phonenumbers.NumberParseException: 67 | return "Enter a valid phone number in International Format" 68 | 69 | # Function to validate OTP 70 | 71 | 72 | def validate_otp(input): 73 | if len(input) != 6 or not input.isdigit(): 74 | return "Enter a valid 6-digit OTP." 75 | return True 76 | 77 | 78 | def check_for_file(): 79 | if not os.path.isfile(authKeyFilePath): 80 | return False 81 | 82 | try: 83 | with open(authKeyFilePath) as file: 84 | json.load(file) 85 | return True 86 | except (ValueError, IOError): 87 | return False 88 | 89 | # Function to perform the login process 90 | 91 | 92 | def loginFuntion(): 93 | print( 94 | f"{Fore.YELLOW}{Style.BRIGHT}Login\n\n Enter mobile number in International Format\n Example : {Fore.MAGENTA}+919912345678{Fore.YELLOW}.\n" 95 | ) 96 | 97 | questions = [ 98 | { 99 | "type": "text", 100 | "name": "phonenumber", 101 | "message": "Enter your phone number:", 102 | "validate": lambda input: validate_phone_number(input), 103 | } 104 | ] 105 | inputNumber = questionary.prompt(questions) 106 | 107 | try: 108 | pn = phonenumbers.parse(inputNumber["phonenumber"], None) 109 | except phonenumbers.NumberParseException: 110 | print("Enter a valid phone number in International Format") 111 | exit(1) 112 | 113 | response = None 114 | new_req = True 115 | 116 | if os.path.exists(requestFilePath): 117 | with open(requestFilePath, "r") as file: 118 | fileData = json.load(file) 119 | if ( 120 | "parsedPhoneNumber" in fileData 121 | and f"+{fileData['parsedPhoneNumber']}" == inputNumber["phonenumber"] 122 | ): 123 | print( 124 | f"{Fore.MAGENTA}\nPrevious request was found for this mobile number.\n" 125 | ) 126 | x = questionary.confirm( 127 | "Do you want to enter previous OTP?").ask() 128 | 129 | if x: 130 | new_req = False 131 | response = fileData 132 | 133 | if new_req: 134 | response = asyncio.run(login(str(inputNumber["phonenumber"]))) 135 | print( 136 | f"{Fore.YELLOW}Sending OTP to {Fore.GREEN}{inputNumber['phonenumber']}{Fore.YELLOW}." 137 | ) 138 | 139 | 140 | if ( 141 | response["data"]["status"] == 1 142 | or response["data"]["status"] == 9 143 | or response["data"]["message"] == "Sent" 144 | ): 145 | with open(requestFilePath, "w") as file: 146 | json.dump(response["data"], file, indent=4) 147 | 148 | if new_req: 149 | print(f"{Fore.GREEN}OTP sent successfully.") 150 | 151 | questions1 = [ 152 | { 153 | "type": "text", 154 | "name": "otp", 155 | "message": "Enter Received OTP:", 156 | "validate": lambda input: validate_otp(input), 157 | } 158 | ] 159 | 160 | token = questionary.prompt(questions1) 161 | 162 | response1 = asyncio.run(verify_otp( 163 | str(inputNumber["phonenumber"]), 164 | response["data"], 165 | token["otp"], 166 | )) 167 | 168 | if "status" in response1["data"] and response1["data"]["status"] == 2 and "suspended" in response1["data"] and not response1["data"]["suspended"]: 169 | print( 170 | f"{Fore.YELLOW}{Style.BRIGHT}Your installationId: {Fore.GREEN}{response1['data']['installationId']}" 171 | ) 172 | 173 | with open(authKeyFilePath, "w") as file: 174 | json.dump(response1["data"], file, indent=3) 175 | 176 | print(f"{Fore.GREEN}Logged in successfully.") 177 | os.remove(requestFilePath) 178 | elif "status" in response1['data'] and response1["data"]["status"] == 11 or response1["data"]["status"] == 40101: 179 | print(f"{Fore.RED}! Invalid OTP") 180 | print( 181 | f"OTP not valid. Enter the 6-digit OTP received on {inputNumber['phonenumber']}." 182 | ) 183 | elif "status" in response1["data"] and response1["data"]["status"] == 7: 184 | print(f"{Fore.RED}Retries limit exceeded") 185 | print( 186 | f"Retries on secret code reached for {inputNumber['phonenumber']}." 187 | ) 188 | elif "suspended" in response1["data"] and response1["data"]["suspended"] == True: 189 | print(f"{Fore.RED}Oops... Your account is suspended.") 190 | print("Your account has been suspended by Truecaller.") 191 | elif "message" in response1["data"]: 192 | print(f"{Fore.RED}{response1['data']['message']}") 193 | else: 194 | print(json.dumps(response1, indent=4)) 195 | elif "status" in response and response["data"]["status"] == 6 or response["data"]["status"] == 5: 196 | if os.path.exists(requestFilePath): 197 | os.remove(requestFilePath) 198 | print( 199 | f"{Fore.RED}You have exceeded the limit of verification attempts.\nPlease try again after some time." 200 | ) 201 | else: 202 | print(f"{Fore.RED}{response['data']['message']}") 203 | 204 | 205 | def searcFunction(args): 206 | if not check_for_file(): 207 | print(Fore.MAGENTA + Style.BRIGHT + 208 | "Please login to your account." + Style.RESET_ALL) 209 | sys.exit() 210 | 211 | try: 212 | with open(authKeyFilePath, "r") as auth_key_file: 213 | data = auth_key_file.read() 214 | json_auth_key = json.loads(data) 215 | country_code = json_auth_key["phones"][0]["countryCode"] 216 | installation_id = json_auth_key["installationId"] 217 | 218 | # Perform the search operation 219 | search_result = asyncio.run(search_phonenumber( 220 | args.search, country_code, installation_id)) 221 | 222 | if args.name and not args.email: 223 | try: 224 | name = search_result["data"]['data'][0]['name'] 225 | except (AttributeError, IndexError, KeyError): 226 | name = "Unknown Name" 227 | 228 | print( 229 | name if args.raw else f"{Fore.BLUE + Style.BRIGHT}Name: {Fore.GREEN}{name}{Style.RESET_ALL}") 230 | 231 | elif not args.name and args.email: 232 | try: 233 | data = search_result["data"].get("data") 234 | if data and len(data) > 0: 235 | internet_addresses = data[0].get("internetAddresses") 236 | if internet_addresses and len(internet_addresses) > 0: 237 | email = internet_addresses[0].get("id") 238 | else: 239 | email = "Unknown Email" 240 | else: 241 | email = "Unknown Email" 242 | 243 | except (AttributeError, IndexError, KeyError): 244 | email = "Unknown Email" 245 | 246 | print( 247 | email if args.raw else f"{Fore.BLUE + Style.BRIGHT}Email: {Fore.GREEN}{email}{Style.RESET_ALL}") 248 | else: 249 | print(search_result if args.raw else json.dumps( 250 | search_result, indent=2)) 251 | except Exception as error: 252 | print(Fore.RED + str(error) + Style.RESET_ALL) 253 | 254 | 255 | def bulkSearchFunction(args): 256 | if not check_for_file(): 257 | print(Fore.MAGENTA + Style.BRIGHT + 258 | "Please login to your account." + Style.RESET_ALL) 259 | sys.exit() 260 | try: 261 | with open(authKeyFilePath, "r") as auth_key_file: 262 | data = auth_key_file.read() 263 | json_auth_key = json.loads(data) 264 | 265 | countryCode = json_auth_key["phones"][0]["countryCode"] 266 | installationId = json_auth_key["installationId"] 267 | 268 | # Perform bulk search operation 269 | search_result = asyncio.run(bulk_search( 270 | str(args.bs), countryCode, installationId)) 271 | 272 | print(json.dumps(search_result) 273 | if args.raw else json.dumps(search_result, indent=2)) 274 | 275 | except Exception as error: 276 | print(error) 277 | sys.exit(1) 278 | 279 | 280 | def main(): 281 | parser = argparse.ArgumentParser( 282 | usage="\n%(prog)s login (Login to Truecaller).\n%(prog)s -s [number] (command to search a number)." 283 | ) 284 | subparsers = parser.add_subparsers(dest="command") 285 | subparsers.add_parser("login", help="Login to Truecaller") 286 | 287 | parser.add_argument("-s", "--search", metavar="NUMBER", 288 | help="Phone number to search") 289 | 290 | parser.add_argument( 291 | "-r", "--raw", help="Print raw output", action="store_true") 292 | parser.add_argument("--bs", "--bulksearch", metavar="NUMBERS", 293 | help="Make a bulk number search") 294 | parser.add_argument( 295 | "-e", "--email", help="Print email assigned to the phone number", action="store_true") 296 | parser.add_argument( 297 | "-n", "--name", help="Print name assigned to the phone number", action="store_true") 298 | 299 | parser.add_argument("-i", "--installationid", 300 | help="Show your InstallationId", action="store_true") 301 | parser.add_argument("-v", "--verbose", 302 | help="Show additional information", action="count") 303 | 304 | args = parser.parse_args() 305 | 306 | if args.command == "login": 307 | loginFuntion() 308 | elif args.search: 309 | searcFunction(args) 310 | elif args.bs: 311 | bulkSearchFunction(args) 312 | elif args.installationid: 313 | if not check_for_file(): 314 | print(Fore.MAGENTA + Style.BRIGHT + 315 | "Please login to your account." + Style.RESET_ALL) 316 | sys.exit() 317 | 318 | try: 319 | with open(authKeyFilePath, "r") as auth_key_file: 320 | data = auth_key_file.read() 321 | json_auth_key = json.loads(data) 322 | 323 | installationId = json_auth_key["installationId"] 324 | 325 | if args.raw: 326 | print(installationId) 327 | else: 328 | print(Fore.BLUE + Style.BRIGHT + "Your InstallationId: " + 329 | Style.RESET_ALL + Fore.GREEN + installationId + Style.RESET_ALL) 330 | except Exception as error: 331 | print(Fore.RED + "An error occurred." + Style.RESET_ALL) 332 | print(error) 333 | sys.exit(1) 334 | else: 335 | parser.print_help() 336 | 337 | 338 | if __name__ == "__main__": 339 | main() 340 | --------------------------------------------------------------------------------