├── 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 | [](https://pypi.org/project/truecallerpy)
4 | [](https://pypi.org/project/truecallerpy)
5 | [](https://pepy.tech/project/truecallerpy)
6 | [](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 |
216 |
217 | 
218 |
219 | ### 💖 Sponsors
220 |
221 | [](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 |
--------------------------------------------------------------------------------