├── .gitignore ├── README.md ├── locate.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.session 2 | *.session-journal 3 | map.html -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Telegram Locator 2 | Python script that creates a precise map of people around you. 3 | 4 | ## Features: 5 | - Collects data using **people nearby** telegram feature 6 | - [Trilaterates](https://en.wikipedia.org/wiki/True_range_multilateration) the exact position of each person 7 | - Renders all the results on *map.html* 8 | 9 | ### Example: 10 | [![example.jpg](https://i.postimg.cc/7Zcjm5kN/example.jpg)](https://postimg.cc/DSrCzymW) 11 | 12 | ### Usage: 13 | 1. Install dependencies: 14 | ```sh 15 | python -m pip install -r ./requirements.txt 16 | ``` 17 | 2. Run script: 18 | ```sh 19 | python ./locate.py --number NUMBER --latitude LATITUDE --longitude LONGITUDE [--offset OFFSET] [--help] 20 | ``` 21 | 3. Authenticate (if prompted): 22 | ``` 23 | Code: 24 | Password: 25 | ``` 26 | 27 | ### Arguments: 28 | | Long | Short | Description | 29 | | --------------------- | ------------- | ---------------------------------------------------------- | 30 | | --help | -h | Show help message | 31 | | --number NUMBER | -n NUMBER | Telephone number (aka Telegram login) | 32 | | --latitude LATITUDE | -la LATITUDE | Latitude of your starting location | 33 | | --longitude LONGITUDE | -lo LONGITUDE | Longitude of your starting location | 34 | | --offset OFFSET | -o OFFSET | Trilateration scanning offset in degrees (default: 0.0007) | 35 | 36 | ### Third-party libraries: 37 | * [Telethon](https://github.com/LonamiWebs/Telethon) - Pure Python 3 MTProto API Telegram client library. 38 | * [gmplot](https://github.com/gmplot/gmplot) - A matplotlib-like interface to render all the data you'd like on top of Google Maps. -------------------------------------------------------------------------------- /locate.py: -------------------------------------------------------------------------------- 1 | # Imports 2 | import gmplot 3 | from gmplot.writer import _Writer 4 | from telethon.sync import TelegramClient, functions, types 5 | from telethon.errors import SessionPasswordNeededError 6 | import argparse 7 | import getpass 8 | from random import randrange 9 | import json 10 | from time import sleep 11 | import math 12 | 13 | # Argument parser 14 | parser = argparse.ArgumentParser() 15 | parser.add_argument("--number", "-n", dest="NUMBER", 16 | help="Telephone number", required=True) 17 | parser.add_argument("--latitude", "-la", dest="LATITUDE", type=float, 18 | help="Latitude of your starting location", required=True) 19 | parser.add_argument("--longitude", "-lo", dest="LONGITUDE", type=float, 20 | help="Longitude of your starting location", required=True) 21 | parser.add_argument("--offset", "-o", dest="OFFSET", type=float, 22 | help="Trilateration scanning offset", default=0.0007) 23 | 24 | args = parser.parse_args() 25 | 26 | # Authentication 27 | api_id = 760605 28 | api_hash = "5ea1a2f93b1d038e328c012846a35b13" 29 | number = args.NUMBER 30 | 31 | client = TelegramClient("location", api_id, api_hash) 32 | client.connect() 33 | 34 | if not client.is_user_authorized(): 35 | client.send_code_request(number) 36 | code = input("Code: ") 37 | password = getpass.getpass("Password: ") 38 | try: 39 | client.sign_in(number, code) 40 | except SessionPasswordNeededError: 41 | client.sign_in(password=getpass.getpass()) 42 | 43 | 44 | def locate(lat, long): 45 | request = functions.contacts.GetLocatedRequest( 46 | geo_point=types.InputGeoPoint(lat, long), 47 | self_expires=1 48 | ) 49 | 50 | result = client(request) 51 | 52 | users = [] 53 | for user in result.users: 54 | name = str(user.first_name) 55 | if user.last_name is not None: 56 | name += " " + str(user.last_name) 57 | 58 | distance = -1 59 | for peer in result.updates[0].peers: 60 | if not isinstance(peer, types.PeerLocated): 61 | continue 62 | if peer.peer.user_id == user.id: 63 | distance = peer.distance 64 | break 65 | 66 | if (distance != -1): 67 | users.append({ 68 | "name": name, 69 | "distance": distance, 70 | "id": user.id 71 | }) 72 | 73 | return users 74 | 75 | 76 | # Locating 77 | cords = [] 78 | centers = [] 79 | 80 | la = args.LATITUDE 81 | lo = args.LONGITUDE 82 | offset = args.OFFSET 83 | 84 | R = 6378.137 85 | dLat = (la) * math.pi / 180 - (la + offset) * math.pi / 180 86 | a = math.sin(dLat/2) ** 2 87 | c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) 88 | dx = R * c * 1000 89 | 90 | dLon = (lo) * math.pi / 180 - (lo + offset) * math.pi / 180 91 | a = math.cos(la * math.pi / 180) ** 2 * math.sin(dLon/2) ** 2 92 | c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) 93 | dy = R * c * 1000 94 | 95 | print("Scanning at the starting point...", flush=True) 96 | centers.append((la, lo)) 97 | users = locate(la, lo) 98 | cords.append(users) 99 | sleep(dx / 10) 100 | 101 | print("Scanning at the offset point...", flush=True) 102 | centers.append((la + offset, lo)) 103 | users = locate(la + offset, lo) 104 | cords.append(users) 105 | sleep((dy ** 2 + dx ** 2) ** (1/2) / 10) 106 | 107 | print("Scanning at the callibration point...", flush=True) 108 | centers.append((la, lo + offset)) 109 | users = locate(la, lo + offset) 110 | cords.append(users) 111 | 112 | ''' 113 | # OLD CODE FOR MANUAL TRILATERATION 114 | for i, user in enumerate(cords[0]): 115 | print(str(i) + ") " + user["name"]) 116 | 117 | while True: 118 | id = int(input("ID: ")) 119 | print("Locating " + cords[0][id]["name"] + 120 | " (#" + str(cords[0][id]["id"]) + ")") 121 | id = cords[0][id]["id"] 122 | 123 | gmap = gmplot.GoogleMapPlotter(la, lo, 16) 124 | 125 | for i, users in enumerate(cords): 126 | for user in users: 127 | if (user["id"] == id): 128 | print("Distance: " + str(user["distance"]) + "m") 129 | gmap.circle( 130 | centers[i][0], 131 | centers[i][1], 132 | user["distance"], 133 | face_alpha=0, 134 | edge_color="#ff0000" 135 | ) 136 | break 137 | ''' 138 | 139 | print("Generating map...") 140 | gmap = gmplot.GoogleMapPlotter(la, lo, 16) 141 | 142 | for user in cords[0]: 143 | distances = [] 144 | # Put all user's distances together 145 | for users in cords: 146 | for x in users: 147 | if (user["id"] == x["id"]): 148 | distances.append(x["distance"]) 149 | if (len(distances) < 3): 150 | print("Unable to locate " + user["name"]) 151 | continue 152 | 153 | x = (distances[0] ** 2 - distances[1] ** 2 + dx ** 2) \ 154 | / (2 * dx) 155 | y = (distances[0] ** 2 - x ** 2) ** (1/2) 156 | 157 | # Reverse dx 158 | c = x / (R * 1000) 159 | a = math.tan(c / 2) ** 2 / (1 + math.tan(c / 2) ** 2) 160 | dLat = math.asin(a ** (1/2)) * 2 161 | offsetX = ((la) * math.pi / 180 - dLat) * 180 / math.pi - la 162 | offsetX = math.copysign(offsetX, x) 163 | 164 | # Reverse dy 165 | c = y / (R * 1000) 166 | a = abs(math.tan(abs(c / 2)) ** 2 / (1 + math.tan(abs(c / 2)) ** 2)) 167 | dLon = math.asin((a / math.cos(la * math.pi / 180) ** 2) ** (1/2)) * 2 168 | offsetY = ((lo) * math.pi / 180 - dLon) * 180 / math.pi - lo 169 | 170 | distPos = abs((x ** 2 + (dy + y) ** 2) ** (1/2) - distances[2]) 171 | distNeg = abs((x ** 2 + (dy - y) ** 2) ** (1/2) - distances[2]) 172 | if (distNeg < distPos): 173 | offsetY = -offsetY 174 | 175 | gmap.marker( 176 | la + offsetX, 177 | lo + offsetY, 178 | label=user["name"][0], 179 | info_window=user["name"].replace("\"", "") + 180 | " [±" + f"{min(distPos, distNeg):9.2f}" + "m]", 181 | title=user["name"].replace("\"", ""), 182 | color="cyan" 183 | ) 184 | 185 | print("Done!") 186 | # Google's free API key 187 | gmap.apikey = "AIzaSyDeRNMnZ__VnQDiATiuz4kPjF_c9r1kWe8" 188 | with open("map.html", 'w', encoding="utf-8") as f: 189 | with _Writer(f) as w: 190 | gmap._write_html(w) 191 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | gmplot==1.4.1 2 | Telethon==1.24.0 --------------------------------------------------------------------------------