1 | 1 | | from colorama import Fore, Style |
2 | 2 | | from googletrans import Translator |
3 | | - | from telepathy.const import __version__, user_agent |
| 3 | + | from src.telepathy.const import __version__, user_agent |
4 | 4 | | import requests |
5 | 5 | | import textwrap |
6 | 6 | | from bs4 import BeautifulSoup |
7 | 7 | | import random |
| 8 | + | import os |
| 9 | + | |
8 | 10 | | |
9 | 11 | | def createPlaceholdeCls(): |
10 | 12 | | class Object(object): |
11 | 13 | | pass |
| 14 | + | |
12 | 15 | | a = Object() |
13 | 16 | | return a |
| 17 | + | |
14 | 18 | | |
15 | 19 | | def print_banner(): |
16 | 20 | | print( |
| skipped 6 lines |
23 | 27 | | /_/ \___/_/\___/ .___/\__,_/\__/_/ /_/\__, / |
24 | 28 | | /_/ /____/ |
25 | 29 | | -- An OSINT toolkit for investigating Telegram chats. |
26 | | - | -- Developed by @jordanwildon | Version """ + __version__ + """. |
27 | | - | """ + Style.RESET_ALL |
| 30 | + | -- Developed by @jordanwildon | Version """ |
| 31 | + | + __version__ |
| 32 | + | + """. |
| 33 | + | """ |
| 34 | + | + Style.RESET_ALL |
28 | 35 | | ) |
| 36 | + | |
29 | 37 | | |
30 | 38 | | def parse_tg_date(dd): |
31 | 39 | | year = str(format(dd.year, "02d")) |
| skipped 5 lines |
37 | 45 | | date = year + "-" + month + "-" + day |
38 | 46 | | mtime = hour + ":" + minute + ":" + second |
39 | 47 | | timestamp = date + "T" + mtime + "+00:00" |
40 | | - | return {"timestamp":timestamp, "date":date, "mtime":mtime} |
| 48 | + | return {"timestamp": timestamp, "date": date, "mtime": mtime} |
41 | 49 | | |
42 | 50 | | |
43 | 51 | | def populate_user(user, group_or_chat): |
| skipped 47 lines |
91 | 99 | | "message_text": mess_txt, |
92 | 100 | | } |
93 | 101 | | |
| 102 | + | |
94 | 103 | | def process_description(desc, user_lang): |
95 | 104 | | if desc is not None: |
96 | 105 | | desc_txt = '"' + desc + '"' |
| skipped 19 lines |
116 | 125 | | "description_text": desc_txt, |
117 | 126 | | } |
118 | 127 | | |
119 | | - | def color_print_green(first_string,second_string): |
120 | | - | print( |
121 | | - | Fore.GREEN |
122 | | - | + first_string |
123 | | - | + Style.RESET_ALL |
124 | | - | + second_string |
125 | | - | ) |
| 128 | + | |
| 129 | + | def color_print_green(first_string, second_string): |
| 130 | + | print(Fore.GREEN + first_string + Style.RESET_ALL + second_string) |
| 131 | + | |
126 | 132 | | |
127 | 133 | | def parse_html_page(url): |
128 | 134 | | s = requests.Session() |
| skipped 7 lines |
136 | 142 | | group_description = "" |
137 | 143 | | total_participants = "" |
138 | 144 | | try: |
139 | | - | name = soup.find( |
140 | | - | "div", {"class": ["tgme_page_title"]} |
141 | | - | ).text |
| 145 | + | name = soup.find("div", {"class": ["tgme_page_title"]}).text |
142 | 146 | | except: |
143 | 147 | | name = "Not found" |
144 | 148 | | try: |
145 | | - | group_description = soup.find( |
146 | | - | "div", {"class": ["tgme_page_description"]} |
147 | | - | ).text |
148 | | - | descript = Fore.GREEN + "Description: " + Style.RESET_ALL+ group_description |
| 149 | + | group_description = ( |
| 150 | + | soup.find("div", {"class": ["tgme_page_description"]}) |
| 151 | + | .getText(separator="\n") |
| 152 | + | .replace("\n", " ") |
| 153 | + | ) |
| 154 | + | # descript = Fore.GREEN + "Description: " + Style.RESET_ALL + group_description |
149 | 155 | | except: |
150 | 156 | | group_description = "None" |
151 | | - | descript = Fore.GREEN + "Description: " + Style.RESET_ALL+ group_description |
152 | | - | |
| 157 | + | # descript = Fore.GREEN + "Description: " + Style.RESET_ALL + group_description |
153 | 158 | | try: |
154 | | - | group_participants = soup.find( |
155 | | - | "div", {"class": ["tgme_page_extra"]} |
156 | | - | ).text |
| 159 | + | group_participants = soup.find("div", {"class": ["tgme_page_extra"]}).text |
157 | 160 | | sep = "members" |
158 | 161 | | stripped = group_participants.split(sep, 1)[0] |
159 | 162 | | total_participants = ( |
| skipped 4 lines |
164 | 167 | | ) |
165 | 168 | | except: |
166 | 169 | | total_participants = "Not found" |
167 | | - | |
168 | | - | return {"name":name,"group_description":group_description, "total_participants":total_participants} |
| 170 | + | return { |
| 171 | + | "name": name, |
| 172 | + | "group_description": group_description, |
| 173 | + | "total_participants": total_participants, |
| 174 | + | } |
169 | 175 | | |
170 | 176 | | |
171 | 177 | | def generate_textwrap(text_string, size=70): |
| skipped 5 lines |
177 | 183 | | subsequent_indent=" ", |
178 | 184 | | ) |
179 | 185 | | |
| 186 | + | |
180 | 187 | | def print_shell(type, obj): |
| 188 | + | if type == "bot": |
| 189 | + | color_print_green(" [+] ", "Bot details for " + str(obj.id)) |
| 190 | + | color_print_green(f" ├ Username: @{str(obj.username)}") |
| 191 | + | color_print_green(f" ├ Name: {str(obj.first_name)}") |
| 192 | + | color_print_green(f" └ Link: https://t.me/{str(obj.username)}") |
| 193 | + | print("\n") |
| 194 | + | color_print_green(" [+] ", "User related details for bot " + str(obj.id)) |
| 195 | + | color_print_green(f" ├ User id: @{str(obj.user_id)}") |
| 196 | + | color_print_green(f" ├ User username: @{str(obj.user_username)}") |
| 197 | + | color_print_green(f" ├ User name: {str(obj.user_first_name)}") |
| 198 | + | color_print_green(f" └ Link: https://t.me/{str(obj.user_username)}") |
| 199 | + | |
181 | 200 | | if type == "user": |
182 | 201 | | color_print_green(" [+] ", "User details for " + obj.target) |
| 202 | + | color_print_green(" ├ Id: ", str(obj.id)) |
183 | 203 | | color_print_green(" ├ Username: ", str(obj.username)) |
184 | 204 | | color_print_green(" ├ Name: ", str(obj.user_full_name)) |
185 | 205 | | color_print_green(" ├ Verification: ", str(obj.verified)) |
186 | 206 | | color_print_green(" ├ Photo ID: ", str(obj.user_photo)) |
187 | 207 | | color_print_green(" ├ Phone number: ", str(obj.phone)) |
188 | | - | color_print_green( |
189 | | - | " ├ Access hash: ", str(obj.access_hash) |
190 | | - | ) |
| 208 | + | color_print_green(" ├ Access hash: ", str(obj.access_hash)) |
191 | 209 | | color_print_green(" ├ Language: ", str(obj.lang_code)) |
192 | 210 | | color_print_green(" ├ Bot: ", str(obj.bot)) |
193 | 211 | | color_print_green(" ├ Scam: ", str(obj.scam)) |
| skipped 13 lines |
207 | 225 | | |
208 | 226 | | d_wrapper = generate_textwrap("Description:") |
209 | 227 | | td_wrapper = generate_textwrap("Translated Description:") |
210 | | - | |
211 | 228 | | color_print_green(" ┬ Chat details", "") |
212 | 229 | | color_print_green(" ├ Title: ", str(obj.title)) |
213 | 230 | | color_print_green(" ├ ", d_wrapper.fill(obj.group_description)) |
214 | 231 | | if obj.translated_description != obj.group_description: |
215 | 232 | | color_print_green(" ├ ", td_wrapper.fill(obj.translated_description)) |
216 | | - | color_print_green( |
217 | | - | " ├ Total participants: ", str(obj.total_participants) |
218 | | - | ) |
| 233 | + | color_print_green(" ├ Total participants: ", str(obj.total_participants)) |
219 | 234 | | |
220 | 235 | | if type == "group_recap": |
221 | 236 | | color_print_green( |
| skipped 9 lines |
231 | 246 | | color_print_green(" ├ Chat type: ", str(obj.chat_type)) |
232 | 247 | | color_print_green(" ├ Chat id: ", str(obj.id)) |
233 | 248 | | color_print_green(" ├ Access hash: ", str(obj.access_hash)) |
234 | | - | if type == "channel_recap": |
| 249 | + | if type == "channel_recap": |
235 | 250 | | scam_status = str(obj.scam) |
236 | 251 | | color_print_green(" ├ Scam: ", str(scam_status)) |
237 | 252 | | color_print_green(" ├ First post date: ", str(obj.first_post)) |
238 | 253 | | if type == "group_recap": |
239 | | - | color_print_green( |
240 | | - | " ├ Memberlist saved to: ", obj.memberlist_filename |
241 | | - | ) |
242 | | - | color_print_green( |
243 | | - | " └ Restrictions: ", (str(obj.group_status)) |
244 | | - | ) |
| 254 | + | color_print_green(" ├ Memberlist saved to: ", obj.memberlist_filename) |
| 255 | + | color_print_green(" └ Restrictions: ", (str(obj.group_status))) |
245 | 256 | | |
246 | 257 | | if type == "group_stat": |
247 | 258 | | color_print_green(" [+] Chat archive saved", "") |
248 | 259 | | color_print_green(" ┬ Chat statistics", "") |
249 | | - | color_print_green( |
250 | | - | " ├ Number of messages found: ", str(obj.messages_found) |
251 | | - | ) |
252 | | - | color_print_green( |
253 | | - | " ├ Top poster 1: ", str(obj.poster_one) |
254 | | - | ) |
255 | | - | color_print_green( |
256 | | - | " ├ Top poster 2: ", str(obj.poster_two) |
257 | | - | ) |
258 | | - | color_print_green( |
259 | | - | " ├ Top poster 3: ", str(obj.poster_three) |
260 | | - | ) |
261 | | - | color_print_green( |
262 | | - | " ├ Top poster 4: ", str(obj.poster_four) |
263 | | - | ) |
264 | | - | color_print_green( |
265 | | - | " ├ Top poster 5: ", str(obj.poster_five) |
266 | | - | ) |
267 | | - | color_print_green( |
268 | | - | " ├ Total unique posters: ", str(obj.unique_active) |
269 | | - | ) |
270 | | - | color_print_green( |
271 | | - | " └ Archive saved to: ", str(obj.file_archive) |
272 | | - | ) |
| 260 | + | color_print_green(" ├ Number of messages found: ", str(obj.messages_found)) |
| 261 | + | color_print_green(" ├ Top poster 1: ", str(obj.poster_one)) |
| 262 | + | color_print_green(" ├ Top poster 2: ", str(obj.poster_two)) |
| 263 | + | color_print_green(" ├ Top poster 3: ", str(obj.poster_three)) |
| 264 | + | color_print_green(" ├ Top poster 4: ", str(obj.poster_four)) |
| 265 | + | color_print_green(" ├ Top poster 5: ", str(obj.poster_five)) |
| 266 | + | color_print_green(" ├ Total unique posters: ", str(obj.unique_active)) |
| 267 | + | color_print_green(" └ Archive saved to: ", str(obj.file_archive)) |
273 | 268 | | return |
274 | 269 | | |
275 | 270 | | if type == "channel_stat": |
276 | 271 | | color_print_green(" [+] Channel archive saved", "") |
277 | 272 | | color_print_green(" ┬ Channel statistics", "") |
278 | | - | color_print_green( |
279 | | - | " ├ Number of messages found: ", str(obj.messages_found) |
280 | | - | ) |
281 | | - | color_print_green( |
282 | | - | " └ Archive saved to: ", str(obj.file_archive) |
283 | | - | ) |
| 273 | + | color_print_green(" ├ Number of messages found: ", str(obj.messages_found)) |
| 274 | + | color_print_green(" └ Archive saved to: ", str(obj.file_archive)) |
284 | 275 | | return |
285 | 276 | | |
286 | 277 | | if type == "reply_stat": |
| skipped 12 lines |
299 | 290 | | " └ Active members list who replied to messages, saved to: ", |
300 | 291 | | str(obj.reply_memberlist_filename), |
301 | 292 | | ) |
302 | | - | color_print_green( |
303 | | - | " ┬ Top replier 1: ", str(obj.replier_one) |
304 | | - | ) |
305 | | - | color_print_green( |
306 | | - | " ├ Top replier 2: ", str(obj.replier_two) |
307 | | - | ) |
308 | | - | color_print_green( |
309 | | - | " ├ Top replier 3: ", str(obj.replier_three) |
310 | | - | ) |
311 | | - | color_print_green( |
312 | | - | " ├ Top replier 4: ", str(obj.replier_four) |
313 | | - | ) |
314 | | - | color_print_green( |
315 | | - | " ├ Top replier 5: ", str(obj.replier_five) |
316 | | - | ) |
317 | | - | color_print_green( |
318 | | - | " └ Total unique repliers: ", str(obj.replier_unique) |
319 | | - | ) |
| 293 | + | color_print_green(" ┬ Top replier 1: ", str(obj.replier_one)) |
| 294 | + | color_print_green(" ├ Top replier 2: ", str(obj.replier_two)) |
| 295 | + | color_print_green(" ├ Top replier 3: ", str(obj.replier_three)) |
| 296 | + | color_print_green(" ├ Top replier 4: ", str(obj.replier_four)) |
| 297 | + | color_print_green(" ├ Top replier 5: ", str(obj.replier_five)) |
| 298 | + | color_print_green(" └ Total unique repliers: ", str(obj.replier_unique)) |
320 | 299 | | |
321 | 300 | | if type == "forwarder_stat": |
322 | 301 | | color_print_green(" [+] Forward scrape complete", "") |
323 | 302 | | color_print_green(" ┬ Statistics", "") |
324 | | - | color_print_green( |
325 | | - | " ├ Forwarded messages found: ", str(obj.forward_count) |
326 | | - | ) |
| 303 | + | color_print_green(" ├ Forwarded messages found: ", str(obj.forward_count)) |
327 | 304 | | color_print_green( |
328 | 305 | | " ├ Forwards from active public chats: ", |
329 | 306 | | str(obj.forwards_found), |
| skipped 3 lines |
333 | 310 | | " ├ Forwards from private (or now private) chats: ", |
334 | 311 | | str(obj.private_count), |
335 | 312 | | ) |
336 | | - | color_print_green( |
337 | | - | " ├ Unique forward sources: ", str(obj.unique_forwards) |
338 | | - | ) |
339 | | - | color_print_green( |
340 | | - | " ├ Top forward source 1: ", str(obj.forward_one) |
341 | | - | ) |
342 | | - | color_print_green( |
343 | | - | " ├ Top forward source 2: ", str(obj.forward_two) |
344 | | - | ) |
345 | | - | color_print_green( |
346 | | - | " ├ Top forward source 3: ", str(obj.forward_three) |
347 | | - | ) |
348 | | - | color_print_green( |
349 | | - | " ├ Top forward source 4: ", str(obj.forward_four) |
350 | | - | ) |
351 | | - | color_print_green( |
352 | | - | " ├ Top forward source 5: ", str(obj.forward_five) |
353 | | - | ) |
| 313 | + | color_print_green(" ├ Unique forward sources: ", str(obj.unique_forwards)) |
| 314 | + | color_print_green(" ├ Top forward source 1: ", str(obj.forward_one)) |
| 315 | + | color_print_green(" ├ Top forward source 2: ", str(obj.forward_two)) |
| 316 | + | color_print_green(" ├ Top forward source 3: ", str(obj.forward_three)) |
| 317 | + | color_print_green(" ├ Top forward source 4: ", str(obj.forward_four)) |
| 318 | + | color_print_green(" ├ Top forward source 5: ", str(obj.forward_five)) |
354 | 319 | | color_print_green(" └ Edgelist saved to: ", obj.edgelist_file) |
| 320 | + | |
| 321 | + | |
| 322 | + | def create_path(path_d): |
| 323 | + | if not os.path.exists(path_d): |
| 324 | + | os.makedirs(path_d) |
| 325 | + | return path_d |
| 326 | + | |
| 327 | + | |
| 328 | + | def create_file_report(save_dir, name, type, extension, file_time, append_time=True): |
| 329 | + | _time_append = "" |
| 330 | + | if append_time: |
| 331 | + | _time_append = "_" + file_time |
| 332 | + | return os.path.join( |
| 333 | + | "{}".format(save_dir), "{}{}_{}.{}".format(name, _time_append, type, extension) |
| 334 | + | ) |
| 335 | + | |
| 336 | + | |
| 337 | + | def clean_private_invite(url): |
| 338 | + | if type(url) is not int: |
| 339 | + | if "https://t.me/+" in url: |
| 340 | + | return url.replace("https://t.me/+", "https://t.me/joinchat/") |
| 341 | + | return url |
| 342 | + | |
| 343 | + | |
| 344 | + | def evaluate_reactions(message, create=False): |
| 345 | + | total_reactions = 0 |
| 346 | + | reactions = {} |
| 347 | + | if not create and message.reactions: |
| 348 | + | reactions_l = message.reactions.results |
| 349 | + | for idx, i in enumerate(reactions_l): |
| 350 | + | total_reactions = total_reactions + i.count |
| 351 | + | reactions["thumbs_up"] = i.count if i.reaction == "👍" else 0 |
| 352 | + | reactions["thumbs_down"] = i.count if i.reaction == "👎" else 0 |
| 353 | + | reactions["heart"] = i.count if i.reaction == "❤️" else 0 |
| 354 | + | reactions["fire"] = i.count if i.reaction == "🔥" else 0 |
| 355 | + | reactions["smile_with_hearts"] = i.count if i.reaction == "🥰" else 0 |
| 356 | + | reactions["clap"] = i.count if i.reaction == "👏" else 0 |
| 357 | + | reactions["smile"] = i.count if i.reaction == "😁" else 0 |
| 358 | + | reactions["thinking"] = i.count if i.reaction == "🤔" else 0 |
| 359 | + | reactions["exploding_head"] = i.count if i.reaction == "🤯" else 0 |
| 360 | + | reactions["scream"] = i.count if i.reaction == "😱" else 0 |
| 361 | + | reactions["angry"] = i.count if i.reaction == "🤬" else 0 |
| 362 | + | reactions["single_tear"] = i.count if i.reaction == "😢" else 0 |
| 363 | + | reactions["party_popper"] = i.count if i.reaction == "🎉" else 0 |
| 364 | + | reactions["starstruck"] = i.count if i.reaction == "🤩" else 0 |
| 365 | + | reactions["vomiting"] = i.count if i.reaction == "🤮" else 0 |
| 366 | + | reactions["poop"] = i.count if i.reaction == "💩" else 0 |
| 367 | + | reactions["praying"] = i.count if i.reaction == "🙏" else 0 |
| 368 | + | else: |
| 369 | + | reactions["total_reactions"] = "N/A" |
| 370 | + | reactions["thumbs_up"] = "N/A" |
| 371 | + | reactions["thumbs_down"] = "N/A" |
| 372 | + | reactions["heart"] = "N/A" |
| 373 | + | reactions["fire"] = "N/A" |
| 374 | + | reactions["smile_with_hearts"] = "N/A" |
| 375 | + | reactions["clap"] = "N/A" |
| 376 | + | reactions["smile"] = "N/A" |
| 377 | + | reactions["thinking"] = "N/A" |
| 378 | + | reactions["exploding_head"] = "N/A" |
| 379 | + | reactions["scream"] = "N/A" |
| 380 | + | reactions["angry"] = "N/A" |
| 381 | + | reactions["single_tear"] = "N/A" |
| 382 | + | reactions["party_popper"] = "N/A" |
| 383 | + | reactions["starstruck"] = "N/A" |
| 384 | + | reactions["vomiting"] = "N/A" |
| 385 | + | reactions["poop"] = "N/A" |
| 386 | + | reactions["praying"] = "N/A" |
| 387 | + | return total_reactions, reactions |
| 388 | + | |