epstein-files 1.0.16__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- epstein_files/__init__.py +18 -5
- epstein_files/documents/document.py +4 -0
- epstein_files/documents/email.py +12 -12
- epstein_files/epstein_files.py +2 -2
- epstein_files/util/constant/html.py +9 -1
- epstein_files/util/constant/names.py +1 -1
- epstein_files/util/constant/output_files.py +3 -0
- epstein_files/util/env.py +2 -1
- epstein_files/util/highlighted_group.py +6 -4
- epstein_files/util/output.py +30 -2
- epstein_files/util/rich.py +27 -17
- {epstein_files-1.0.16.dist-info → epstein_files-1.1.0.dist-info}/METADATA +1 -1
- {epstein_files-1.0.16.dist-info → epstein_files-1.1.0.dist-info}/RECORD +16 -16
- {epstein_files-1.0.16.dist-info → epstein_files-1.1.0.dist-info}/LICENSE +0 -0
- {epstein_files-1.0.16.dist-info → epstein_files-1.1.0.dist-info}/WHEEL +0 -0
- {epstein_files-1.0.16.dist-info → epstein_files-1.1.0.dist-info}/entry_points.txt +0 -0
epstein_files/__init__.py
CHANGED
|
@@ -16,13 +16,13 @@ from rich.text import Text
|
|
|
16
16
|
from epstein_files.epstein_files import EpsteinFiles, document_cls
|
|
17
17
|
from epstein_files.documents.document import INFO_PADDING, Document
|
|
18
18
|
from epstein_files.documents.email import Email
|
|
19
|
-
from epstein_files.util.constant.output_files import ALL_EMAILS_PATH, TEXT_MSGS_HTML_PATH, make_clean
|
|
19
|
+
from epstein_files.util.constant.output_files import ALL_EMAILS_PATH, CHRONOLOGICAL_EMAILS_PATH, TEXT_MSGS_HTML_PATH, make_clean
|
|
20
20
|
from epstein_files.util.env import args
|
|
21
21
|
from epstein_files.util.file_helper import coerce_file_path, extract_file_id
|
|
22
22
|
from epstein_files.util.logging import logger
|
|
23
23
|
from epstein_files.util.output import (print_emails_section, print_json_files, print_json_stats,
|
|
24
|
-
print_other_files_section, print_text_messages_section, write_json_metadata, write_urls)
|
|
25
|
-
from epstein_files.util.rich import build_highlighter, console,
|
|
24
|
+
print_other_files_section, print_text_messages_section, write_complete_emails_timeline, write_json_metadata, write_urls)
|
|
25
|
+
from epstein_files.util.rich import build_highlighter, console, print_title_page_header, print_title_page_tables, print_panel, write_html
|
|
26
26
|
from epstein_files.util.timer import Timer
|
|
27
27
|
from epstein_files.util.word_count import write_word_counts_html
|
|
28
28
|
|
|
@@ -43,7 +43,10 @@ def generate_html() -> None:
|
|
|
43
43
|
print_json_files(epstein_files)
|
|
44
44
|
exit()
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
print_title_page_header(epstein_files)
|
|
47
|
+
|
|
48
|
+
if not args.email_timeline:
|
|
49
|
+
print_title_page_tables(epstein_files)
|
|
47
50
|
|
|
48
51
|
if args.colors_only:
|
|
49
52
|
exit()
|
|
@@ -55,6 +58,9 @@ def generate_html() -> None:
|
|
|
55
58
|
if args.output_emails:
|
|
56
59
|
emails_that_were_printed = print_emails_section(epstein_files)
|
|
57
60
|
timer.print_at_checkpoint(f"Printed {len(emails_that_were_printed):,} emails")
|
|
61
|
+
elif args.email_timeline:
|
|
62
|
+
write_complete_emails_timeline(epstein_files)
|
|
63
|
+
timer.print_at_checkpoint(f"Printed chronological emails table")
|
|
58
64
|
|
|
59
65
|
if args.output_other:
|
|
60
66
|
if args.uninteresting:
|
|
@@ -66,7 +72,14 @@ def generate_html() -> None:
|
|
|
66
72
|
timer.print_at_checkpoint(f"Printed {len(files)} other files (skipped {len(epstein_files.other_files) - len(files)})")
|
|
67
73
|
|
|
68
74
|
# Save output
|
|
69
|
-
|
|
75
|
+
if args.all_emails:
|
|
76
|
+
output_path = ALL_EMAILS_PATH
|
|
77
|
+
elif args.email_timeline:
|
|
78
|
+
output_path = CHRONOLOGICAL_EMAILS_PATH
|
|
79
|
+
else:
|
|
80
|
+
output_path = TEXT_MSGS_HTML_PATH
|
|
81
|
+
|
|
82
|
+
write_html(output_path)
|
|
70
83
|
logger.warning(f"Total time: {timer.seconds_since_start_str()}")
|
|
71
84
|
|
|
72
85
|
# JSON stats (mostly used for building pytest checks)
|
|
@@ -244,6 +244,10 @@ class Document:
|
|
|
244
244
|
|
|
245
245
|
return (self.timestamp or FALLBACK_TIMESTAMP, sort_id, dupe_idx)
|
|
246
246
|
|
|
247
|
+
def source_file_id(self) -> str:
|
|
248
|
+
"""Strip off the _1, _2, etc. suffixes for extracted documents."""
|
|
249
|
+
return self.file_id[0:6]
|
|
250
|
+
|
|
247
251
|
def summary(self) -> Text:
|
|
248
252
|
"""Summary of this file for logging. Brackets are left open for subclasses to add stuff."""
|
|
249
253
|
txt = Text('').append(self._class_name(), style=self._class_style())
|
epstein_files/documents/email.py
CHANGED
|
@@ -366,7 +366,7 @@ class Email(Communication):
|
|
|
366
366
|
def info_txt(self) -> Text:
|
|
367
367
|
email_type = 'fwded article' if self.is_fwded_article() else 'email'
|
|
368
368
|
txt = Text(f"OCR text of {email_type} from ", style='grey46').append(self.author_txt).append(' to ')
|
|
369
|
-
return txt.append(self.
|
|
369
|
+
return txt.append(self.recipients_txt()).append(highlighter(f" probably sent at {self.timestamp}"))
|
|
370
370
|
|
|
371
371
|
def is_fwded_article(self) -> bool:
|
|
372
372
|
return bool(self.config and self.config.is_fwded_article)
|
|
@@ -382,6 +382,16 @@ class Email(Communication):
|
|
|
382
382
|
metadata.update({k: v for k, v in local_metadata.items() if v and k in METADATA_FIELDS})
|
|
383
383
|
return metadata
|
|
384
384
|
|
|
385
|
+
def recipients_txt(self, max_full_names: int = 2) -> Text:
|
|
386
|
+
"""Text object with comma separated colored versions of all recipients."""
|
|
387
|
+
recipients = [r or UNKNOWN for r in self.recipients] if len(self.recipients) > 0 else [UNKNOWN]
|
|
388
|
+
|
|
389
|
+
# Use just the last name for each recipient if there's 3 or more recipients
|
|
390
|
+
return join_texts([
|
|
391
|
+
Text(r if len(recipients) <= max_full_names else extract_last_name(r), style=get_style_for_name(r))
|
|
392
|
+
for r in recipients
|
|
393
|
+
], join=', ')
|
|
394
|
+
|
|
385
395
|
def subject(self) -> str:
|
|
386
396
|
return self.header.subject or ''
|
|
387
397
|
|
|
@@ -390,7 +400,7 @@ class Email(Communication):
|
|
|
390
400
|
txt = self._summary()
|
|
391
401
|
|
|
392
402
|
if len(self.recipients) > 0:
|
|
393
|
-
txt.append(', ').append(key_value_txt('recipients', self.
|
|
403
|
+
txt.append(', ').append(key_value_txt('recipients', self.recipients_txt()))
|
|
394
404
|
|
|
395
405
|
return txt.append(CLOSE_PROPERTIES_CHAR)
|
|
396
406
|
|
|
@@ -553,16 +563,6 @@ class Email(Communication):
|
|
|
553
563
|
|
|
554
564
|
return collapse_newlines(text).strip()
|
|
555
565
|
|
|
556
|
-
def _recipients_txt(self) -> Text:
|
|
557
|
-
"""Text object with comma separated colored versions of all recipients."""
|
|
558
|
-
recipients = [r or UNKNOWN for r in self.recipients] if len(self.recipients) > 0 else [UNKNOWN]
|
|
559
|
-
|
|
560
|
-
# Use just the last name for each recipient if there's 3 or more recipients
|
|
561
|
-
return join_texts([
|
|
562
|
-
Text(r if len(recipients) < 3 else extract_last_name(r), style=get_style_for_name(r))
|
|
563
|
-
for r in recipients
|
|
564
|
-
], join=', ')
|
|
565
|
-
|
|
566
566
|
def _remove_line(self, idx: int) -> None:
|
|
567
567
|
"""Remove a line from self.lines."""
|
|
568
568
|
num_lines = idx * 2
|
epstein_files/epstein_files.py
CHANGED
|
@@ -29,7 +29,7 @@ from epstein_files.util.env import DOCS_DIR, args, logger
|
|
|
29
29
|
from epstein_files.util.file_helper import file_size_str
|
|
30
30
|
from epstein_files.util.highlighted_group import HIGHLIGHTED_NAMES, HighlightedNames, get_info_for_name, get_style_for_name
|
|
31
31
|
from epstein_files.util.rich import (DEFAULT_NAME_STYLE, LAST_TIMESTAMP_STYLE, NA_TXT, add_cols_to_table,
|
|
32
|
-
print_other_page_link, build_table, console, highlighter, link_text_obj, link_markup,
|
|
32
|
+
print_other_page_link, build_table, console, highlighter, link_text_obj, link_markup, print_author_panel, print_centered,
|
|
33
33
|
print_panel, print_section_header, vertically_pad)
|
|
34
34
|
from epstein_files.util.search_result import SearchResult
|
|
35
35
|
from epstein_files.util.timer import Timer
|
|
@@ -256,7 +256,7 @@ class EpsteinFiles:
|
|
|
256
256
|
unique_emails = [email for email in emails if not email.is_duplicate()]
|
|
257
257
|
author = _author or UNKNOWN
|
|
258
258
|
|
|
259
|
-
|
|
259
|
+
print_author_panel(
|
|
260
260
|
f"Found {len(unique_emails)} {author} emails starting {emails[0].timestamp.date()} over {conversation_length:,} days",
|
|
261
261
|
get_style_for_name(author),
|
|
262
262
|
get_info_for_name(author)
|
|
@@ -5,6 +5,14 @@ from epstein_files.util.env import args
|
|
|
5
5
|
|
|
6
6
|
PAGE_TITLE = ' ∞ Michel de Cryptadamus ∞ '
|
|
7
7
|
|
|
8
|
+
if args.all_emails:
|
|
9
|
+
page_type = 'Emails'
|
|
10
|
+
elif args.email_timeline:
|
|
11
|
+
page_type = 'Chronological Emails'
|
|
12
|
+
else:
|
|
13
|
+
page_type = 'Text Messages'
|
|
14
|
+
|
|
15
|
+
|
|
8
16
|
CONSOLE_HTML_FORMAT = """<!DOCTYPE html>
|
|
9
17
|
<html>
|
|
10
18
|
<head>
|
|
@@ -18,7 +26,7 @@ CONSOLE_HTML_FORMAT = """<!DOCTYPE html>
|
|
|
18
26
|
background-color: {background};
|
|
19
27
|
}}
|
|
20
28
|
</style>
|
|
21
|
-
""" + f"<title>Epstein {
|
|
29
|
+
""" + f"<title>Epstein {page_type}</title>" + """
|
|
22
30
|
</head>
|
|
23
31
|
<body>
|
|
24
32
|
<pre style="font-family: Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace; white-space: pre-wrap; overflow-wrap: break-word;">
|
|
@@ -239,7 +239,7 @@ OTHER_NAMES = NAMES_TO_NOT_HIGHLIGHT + """
|
|
|
239
239
|
gardner gary geoff geoffrey gilbert gloria goldberg gonzalez gould graham greene guarino gwyneth
|
|
240
240
|
hancock harold harrison harry hay helen hill hirsch hofstadter horowitz hussein
|
|
241
241
|
ian isaac isaacson
|
|
242
|
-
james jamie jane janet jason jen jim joe johnson jones josh julie justin
|
|
242
|
+
james jamie jane janet jason jeffrey jen jim joe johnson jones josh julie justin
|
|
243
243
|
karl kate kathy kelly kim kruger kyle
|
|
244
244
|
laurie lawrence leo leonard lenny leslie lieberman louis lynch lynn
|
|
245
245
|
marcus marianne matt matthew melissa michele michelle moore moscowitz
|
|
@@ -6,6 +6,7 @@ from epstein_files.util.constant.strings import EMAIL, TEXT_MESSAGE, SiteType
|
|
|
6
6
|
HTML_DIR = Path('docs')
|
|
7
7
|
EPSTEIN_FILES_NOV_2025 = 'epstein_files_nov_2025'
|
|
8
8
|
ALL_EMAILS_PATH = HTML_DIR.joinpath(f'all_emails_{EPSTEIN_FILES_NOV_2025}.html')
|
|
9
|
+
CHRONOLOGICAL_EMAILS_PATH = HTML_DIR.joinpath(f'chronological_emails_{EPSTEIN_FILES_NOV_2025}.html')
|
|
9
10
|
JSON_FILES_JSON_PATH = HTML_DIR.joinpath(f'json_files_from_{EPSTEIN_FILES_NOV_2025}.json')
|
|
10
11
|
JSON_METADATA_PATH = HTML_DIR.joinpath(f'file_metadata_{EPSTEIN_FILES_NOV_2025}.json')
|
|
11
12
|
TEXT_MSGS_HTML_PATH = HTML_DIR.joinpath('index.html')
|
|
@@ -18,6 +19,7 @@ URLS_ENV = '.urls.env'
|
|
|
18
19
|
GH_PAGES_BASE_URL = 'https://michelcrypt4d4mus.github.io'
|
|
19
20
|
TEXT_MSGS_URL = f"{GH_PAGES_BASE_URL}/epstein_text_messages"
|
|
20
21
|
ALL_EMAILS_URL = f"{TEXT_MSGS_URL}/{ALL_EMAILS_PATH.name}"
|
|
22
|
+
CHRONOLOGICAL_EMAILS_URL = f"{TEXT_MSGS_URL}/{CHRONOLOGICAL_EMAILS_PATH.name}"
|
|
21
23
|
JSON_FILES_URL = f"{TEXT_MSGS_URL}/{JSON_FILES_JSON_PATH.name}"
|
|
22
24
|
JSON_METADATA_URL = f"{TEXT_MSGS_URL}/{JSON_METADATA_PATH.name}"
|
|
23
25
|
WORD_COUNT_URL = f"{TEXT_MSGS_URL}/{WORD_COUNT_HTML_PATH.name}"
|
|
@@ -29,6 +31,7 @@ SITE_URLS: dict[SiteType, str] = {
|
|
|
29
31
|
|
|
30
32
|
BUILD_ARTIFACTS = [
|
|
31
33
|
ALL_EMAILS_PATH,
|
|
34
|
+
CHRONOLOGICAL_EMAILS_PATH,
|
|
32
35
|
# EPSTEIN_WORD_COUNT_HTML_PATH,
|
|
33
36
|
JSON_FILES_JSON_PATH,
|
|
34
37
|
JSON_METADATA_PATH,
|
epstein_files/util/env.py
CHANGED
|
@@ -22,6 +22,7 @@ output = parser.add_argument_group('OUTPUT', 'Options used by epstein_generate.'
|
|
|
22
22
|
output.add_argument('--all-emails', '-ae', action='store_true', help='all the emails instead of just the interesting ones')
|
|
23
23
|
output.add_argument('--all-other-files', '-ao', action='store_true', help='all the non-email, non-text msg files instead of just the interesting ones')
|
|
24
24
|
output.add_argument('--build', '-b', action='store_true', help='write HTML output to a file')
|
|
25
|
+
output.add_argument('--email-timeline', action='store_true', help='print a table of all emails in chronological order')
|
|
25
26
|
output.add_argument('--json-files', action='store_true', help='pretty print all the raw JSON data files in the collection and exit')
|
|
26
27
|
output.add_argument('--json-metadata', action='store_true', help='dump JSON metadata for all files and exit')
|
|
27
28
|
output.add_argument('--output-emails', '-oe', action='store_true', help='generate emails section')
|
|
@@ -70,7 +71,7 @@ args.output_other = args.output_other or args.all_other_files or args.uninterest
|
|
|
70
71
|
args.overwrite_pickle = args.overwrite_pickle or (is_env_var_set('OVERWRITE_PICKLE') and not is_env_var_set('PICKLED'))
|
|
71
72
|
args.width = args.width if is_html_script else None
|
|
72
73
|
is_any_output_selected = any([arg.startswith('output_') and value for arg, value in vars(args).items()])
|
|
73
|
-
is_any_output_selected = is_any_output_selected or args.
|
|
74
|
+
is_any_output_selected = is_any_output_selected or args.colors_only or args.email_timeline or args.json_metadata
|
|
74
75
|
|
|
75
76
|
# Log level args
|
|
76
77
|
if args.deep_debug:
|
|
@@ -143,9 +143,10 @@ HIGHLIGHTED_NAMES = [
|
|
|
143
143
|
HighlightedNames(
|
|
144
144
|
label='Africa',
|
|
145
145
|
style='light_pink4',
|
|
146
|
-
pattern=r'Econet(\s*Wireless)|Ghana(ian)?|Johannesburg|Kenya|Nigerian?|Senegal(ese)?|Serengeti|(South\s*)?African?|(Strive\s*)?Masiyiwa|Tanzania|Ugandan?|Zimbabwe(an)?',
|
|
146
|
+
pattern=r'Buhari|Econet(\s*Wireless)|Ghana(ian)?|Glencore|Goodluck Jonathan|Johannesburg|Kenya|Nigerian?|Okey Enelamah|Senegal(ese)?|Serengeti|(South\s*)?African?|(Strive\s*)?Masiyiwa|Tanzania|Ugandan?|Zimbabwe(an)?',
|
|
147
147
|
emailers={
|
|
148
148
|
'Abdoulaye Wade': 'former president of Senegal, father of Karim Wade',
|
|
149
|
+
'Ivan Glasenberg': "South African former CEO of Glencore, one of the world's largest commodity trading and mining companies",
|
|
149
150
|
'Karim Wade': 'son of the president of Senegal, facing arrest for corruption, email handle is "Afri zp"',
|
|
150
151
|
'Miles Alexander': 'Operations Manager Michaelhouse Balgowan KwaZulu-Natal South Africa', # TODO: what's up with this person?
|
|
151
152
|
'Macky Sall': 'prime minister of Senegal, defeated Abdoulaye Wade',
|
|
@@ -154,8 +155,9 @@ HIGHLIGHTED_NAMES = [
|
|
|
154
155
|
HighlightedNames(
|
|
155
156
|
label='bitcoin',
|
|
156
157
|
style='orange1 bold',
|
|
157
|
-
pattern=r'Balaji|bitcoin|block ?chain(\s*capital)?|Brock(\s*Pierce)?|coins?|cr[iy]?pto(currenc(y|ies))?|e-currency|(Gavin )?Andressen|(Howard\s+)?Lutnic?k|
|
|
158
|
+
pattern=r'Balaji|bitcoin|block ?chain(\s*capital)?|Brock(\s*Pierce)?|coins?|cr[iy]?pto(currenc(y|ies))?|e-currency|(Gavin )?Andressen|(Howard\s+)?Lutnic?k|Libra|Madars|(Patrick\s*)?Murck|(Ross\s*)?Ulbricht|Silk\s*Road|SpanCash|Tether|virtual\s*currenc(ies|y)|(zero\s+knowledge\s+|zk)pro(of|tocols?)',
|
|
158
159
|
emailers = {
|
|
160
|
+
'Jeffrey Wernick': 'former COO of Parler, involved in numerous crypto companies like Bitforex',
|
|
159
161
|
JEREMY_RUBIN: 'developer/researcher',
|
|
160
162
|
ANTHONY_SCARAMUCCI: 'Skybridge Capital, FTX investor',
|
|
161
163
|
},
|
|
@@ -354,7 +356,7 @@ HIGHLIGHTED_NAMES = [
|
|
|
354
356
|
HighlightedNames(
|
|
355
357
|
label='India',
|
|
356
358
|
style='bright_green',
|
|
357
|
-
pattern=r'Abraaj|Anna\s*Hazare|(Arif\s*)?Naqvi|(Arvind\s*)?Kejriwal|Hardeep( Pur[ei]e)?|Indian?|InsightsPod|Modi|Mumbai|Tranchulas',
|
|
359
|
+
pattern=r'Abraaj|Anna\s*Hazare|(Arif\s*)?Naqvi|(Arvind\s*)?Kejriwal|Bangalore|Hardeep( Pur[ei]e)?|Indian?|InsightsPod|Modi|Mumbai|(New\s*)?Delhi|Tranchulas',
|
|
358
360
|
emailers = {
|
|
359
361
|
ANIL_AMBANI: 'chairman of Reliance Group',
|
|
360
362
|
VINIT_SAHNI: None,
|
|
@@ -423,7 +425,7 @@ HIGHLIGHTED_NAMES = [
|
|
|
423
425
|
label='mideast',
|
|
424
426
|
style='dark_sea_green4',
|
|
425
427
|
# something like this won't match ever because of word boundary: [-\s]9/11[\s.]
|
|
426
|
-
pattern=r"Abdulmalik Al-Makhlafi|Abdullah|Abu\s+Dhabi|Afghanistan|Al[-\s]?Qa[ei]da|Ahmadinejad|Arab|Aramco|Assad|Bahrain|Basiji?|Benghazi|Cairo|Chagoury|Dj[iu]bo?uti|Doha|
|
|
428
|
+
pattern=r"Abdulmalik Al-Makhlafi|Abdullah|Abu\s+Dhabi|Afghanistan|Al[-\s]?Qa[ei]da|Ahmadinejad|Arab|Aramco|Assad|Bahrain|Basiji?|Benghazi|Cairo|Chagoury|Dj[iu]bo?uti|Doha|[DB]ubai|Egypt(ian)?|Emir(at(es?|i))?|Erdogan|Fashi|Gaddafi|(Hamid\s*)?Karzai|Hamad\s*bin\s*Jassim|HBJ|Houthi|Imran\s+Khan|Iran(ian)?|Isi[ls]|Islam(abad|ic|ist)?|Istanbul|Kh?ashoggi|(Kairat\s*)?Kelimbetov|kasshohgi|Kaz(akh|ich)stan|Kazakh?|Kh[ao]menei|Khalid\s*Sheikh\s*Mohammed|KSA|Leban(ese|on)|Libyan?|Mahmoud|Marra[hk]e[cs]h|MB(N|S|Z)|Mid(dle)?\s*East|Mohammed\s+bin\s+Salman|Morocco|Mubarak|Muslim|Nayaf|Pakistani?|Omar|(Osama\s*)?Bin\s*Laden|Osama(?! al)|Palestin(e|ian)|Persian?|Riya(dh|nd)|Saddam|Salman|Saudi(\s+Arabian?)?|Shariah?|SHC|sheikh|shia|(Sultan\s*)?Yacoub|Syrian?|(Tarek\s*)?El\s*Sayed|Tehran|Tunisian?|Turk(ey|ish)|UAE|((Iraq|Iran|Kuwait|Qatar|Yemen)i?)",
|
|
427
429
|
emailers = {
|
|
428
430
|
ANAS_ALRASHEED: f'former information minister of Kuwait {QUESTION_MARKS}',
|
|
429
431
|
AZIZA_ALAHMADI: 'Abu Dhabi Department of Culture & Tourism',
|
epstein_files/util/output.py
CHANGED
|
@@ -2,6 +2,7 @@ import json
|
|
|
2
2
|
|
|
3
3
|
from rich.padding import Padding
|
|
4
4
|
|
|
5
|
+
from epstein_files.documents.document import Document
|
|
5
6
|
from epstein_files.documents.email import Email
|
|
6
7
|
from epstein_files.documents.messenger_log import MessengerLog
|
|
7
8
|
from epstein_files.documents.other_file import FIRST_FEW_LINES, OtherFile
|
|
@@ -10,6 +11,7 @@ from epstein_files.util.constant import output_files
|
|
|
10
11
|
from epstein_files.util.constant.html import *
|
|
11
12
|
from epstein_files.util.constant.names import *
|
|
12
13
|
from epstein_files.util.constant.output_files import JSON_FILES_JSON_PATH, JSON_METADATA_PATH
|
|
14
|
+
from epstein_files.util.constant.strings import TIMESTAMP_DIM
|
|
13
15
|
from epstein_files.util.data import dict_sets_to_lists
|
|
14
16
|
from epstein_files.util.env import args
|
|
15
17
|
from epstein_files.util.file_helper import log_file_write
|
|
@@ -27,8 +29,8 @@ DEFAULT_EMAILERS = [
|
|
|
27
29
|
AL_SECKEL,
|
|
28
30
|
DANIEL_SIAD,
|
|
29
31
|
JEAN_LUC_BRUNEL,
|
|
30
|
-
STEVEN_HOFFENBERG,
|
|
31
32
|
RENATA_BOLOTOVA,
|
|
33
|
+
STEVEN_HOFFENBERG,
|
|
32
34
|
MASHA_DROKOVA,
|
|
33
35
|
EHUD_BARAK,
|
|
34
36
|
MARTIN_NOWAK,
|
|
@@ -92,7 +94,7 @@ def print_emails_section(epstein_files: EpsteinFiles) -> list[Email]:
|
|
|
92
94
|
num_emails_printed_since_last_color_key = 0
|
|
93
95
|
|
|
94
96
|
if emailer_tables:
|
|
95
|
-
|
|
97
|
+
print_author_panel(f"Email Tables for {len(emailer_tables)} Other People", 'white')
|
|
96
98
|
|
|
97
99
|
for name in DEFAULT_EMAILER_TABLES:
|
|
98
100
|
epstein_files.print_emails_table_for(name)
|
|
@@ -169,6 +171,32 @@ def print_text_messages_section(epstein_files: EpsteinFiles) -> None:
|
|
|
169
171
|
print_centered(MessengerLog.summary_table(epstein_files.imessage_logs))
|
|
170
172
|
|
|
171
173
|
|
|
174
|
+
def write_complete_emails_timeline(epstein_files: EpsteinFiles) -> None:
|
|
175
|
+
table = build_table('All Non-Junk Emails In Chronological Order', highlight=True)
|
|
176
|
+
table.add_column('ID', style='dim')
|
|
177
|
+
table.add_column('Sent At', style=TIMESTAMP_DIM)
|
|
178
|
+
table.add_column('Author', max_width=22)
|
|
179
|
+
table.add_column('Recipients', max_width=30)
|
|
180
|
+
table.add_column('Length', justify='right', style='wheat4')
|
|
181
|
+
table.add_column('Subject')
|
|
182
|
+
|
|
183
|
+
for email in Document.sort_by_timestamp(epstein_files.non_duplicate_emails()):
|
|
184
|
+
if email.is_junk_mail():
|
|
185
|
+
continue
|
|
186
|
+
|
|
187
|
+
table.add_row(
|
|
188
|
+
email.source_file_id(),
|
|
189
|
+
email.epstein_media_link(link_txt=email.timestamp_without_seconds()),
|
|
190
|
+
email.author_txt,
|
|
191
|
+
email.recipients_txt(max_full_names=1),
|
|
192
|
+
f"{email.length()}",
|
|
193
|
+
email.subject(),
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
console.line(2)
|
|
197
|
+
console.print(table)
|
|
198
|
+
|
|
199
|
+
|
|
172
200
|
def write_json_metadata(epstein_files: EpsteinFiles) -> None:
|
|
173
201
|
json_str = epstein_files.json_metadata()
|
|
174
202
|
|
epstein_files/util/rich.py
CHANGED
|
@@ -14,8 +14,7 @@ from rich.theme import Theme
|
|
|
14
14
|
|
|
15
15
|
from epstein_files.util.constant.html import CONSOLE_HTML_FORMAT, HTML_TERMINAL_THEME, PAGE_TITLE
|
|
16
16
|
from epstein_files.util.constant.names import UNKNOWN
|
|
17
|
-
from epstein_files.util.constant.
|
|
18
|
-
from epstein_files.util.constant.strings import DEFAULT, EMAIL, NA, QUESTION_MARKS, TEXT_MESSAGE, SiteType
|
|
17
|
+
from epstein_files.util.constant.strings import DEFAULT, EMAIL, NA, QUESTION_MARKS, TEXT_MESSAGE
|
|
19
18
|
from epstein_files.util.constant.urls import *
|
|
20
19
|
from epstein_files.util.constants import FALLBACK_TIMESTAMP, HEADER_ABBREVIATIONS
|
|
21
20
|
from epstein_files.util.data import json_safe
|
|
@@ -143,7 +142,8 @@ def parenthesize(msg: str | Text, style: str = '') -> Text:
|
|
|
143
142
|
return Text('(', style=style).append(txt).append(')')
|
|
144
143
|
|
|
145
144
|
|
|
146
|
-
def
|
|
145
|
+
def print_author_panel(msg: str, color: str | None, footer: str | None = None) -> None:
|
|
146
|
+
"""Print a panel with the name of an emailer and a few tidbits of information about them."""
|
|
147
147
|
txt = Text(msg, justify='center')
|
|
148
148
|
color = color or 'white'
|
|
149
149
|
color = 'white' if color == DEFAULT else color
|
|
@@ -180,26 +180,32 @@ def print_color_key() -> None:
|
|
|
180
180
|
print_centered(vertically_pad(color_table))
|
|
181
181
|
|
|
182
182
|
|
|
183
|
-
def
|
|
184
|
-
not_optimized_msg = f"This
|
|
183
|
+
def print_title_page_header(epstein_files: 'EpsteinFiles') -> None:
|
|
184
|
+
not_optimized_msg = f"This page isn't optimized for mobile"
|
|
185
185
|
|
|
186
186
|
if not args.all_emails:
|
|
187
187
|
not_optimized_msg += f" but if you get past the header it should be readable"
|
|
188
188
|
|
|
189
189
|
console.print(f"{not_optimized_msg}.\n", style='dim')
|
|
190
190
|
print_page_title(width=TITLE_WIDTH)
|
|
191
|
-
site_type = EMAIL if args.all_emails else TEXT_MESSAGE
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
other_site_msg
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
191
|
+
site_type = EMAIL if (args.all_emails or args.email_timeline) else TEXT_MESSAGE
|
|
192
|
+
title = f"This is the " + ('chronological ' if args.email_timeline else '') + f"Epstein {site_type.title()}s page"
|
|
193
|
+
print_starred_header(title, num_spaces=4, num_stars=14)
|
|
194
|
+
other_site_msg = "another page with" + (' all of' if other_site_type() == EMAIL else '')
|
|
195
|
+
other_site_msg += f" Epstein's {other_site_type()}s also generated by this code"
|
|
196
|
+
|
|
197
|
+
links = [
|
|
198
|
+
Text.from_markup(link_markup(other_site_url(), other_site_msg, f"{OTHER_SITE_LINK_STYLE} bold")),
|
|
199
|
+
link_text_obj(WORD_COUNT_URL, 'most frequently used words in the emails and texts', AUX_SITE_LINK_STYLE),
|
|
200
|
+
link_text_obj(JSON_METADATA_URL, 'author attribution explanations', AUX_SITE_LINK_STYLE),
|
|
201
|
+
link_text_obj(WORD_COUNT_URL, "epstein's json files", AUX_SITE_LINK_STYLE),
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
for link in links:
|
|
205
|
+
print_centered(parenthesize(link))
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def print_title_page_tables(epstein_files: 'EpsteinFiles') -> None:
|
|
203
209
|
_print_external_links()
|
|
204
210
|
console.line()
|
|
205
211
|
_print_abbreviations_table()
|
|
@@ -274,6 +280,10 @@ def print_other_page_link(epstein_files: 'EpsteinFiles') -> None:
|
|
|
274
280
|
txt.append(" unclassifiable files of particular interest")
|
|
275
281
|
|
|
276
282
|
print_centered(parenthesize(txt), style='dim')
|
|
283
|
+
chrono_emails_markup = link_text_obj(CHRONOLOGICAL_EMAILS_URL, 'a page', style='light_slate_grey bold')
|
|
284
|
+
chrono_emails_txt = Text(f"there's also ").append(chrono_emails_markup)
|
|
285
|
+
chrono_emails_txt.append(' with a table of all the emails in chronological order')
|
|
286
|
+
print_centered(parenthesize(chrono_emails_txt), style='dim')
|
|
277
287
|
|
|
278
288
|
|
|
279
289
|
def print_page_title(expand: bool = True, width: int | None = None) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: epstein-files
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Tools for working with the Jeffrey Epstein documents released in November 2025.
|
|
5
5
|
Home-page: https://michelcrypt4d4mus.github.io/epstein_text_messages/
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
epstein_files/__init__.py,sha256=
|
|
1
|
+
epstein_files/__init__.py,sha256=Roo3XeVQXkgMOQMdq74-dBdkRGeY3sAv7LihZwaGvaY,5538
|
|
2
2
|
epstein_files/documents/communication.py,sha256=oqNsSDWe-N0jSmchIHxpihihzIWha-foFqMwKZlxyng,2057
|
|
3
|
-
epstein_files/documents/document.py,sha256=
|
|
4
|
-
epstein_files/documents/email.py,sha256=
|
|
3
|
+
epstein_files/documents/document.py,sha256=s77ZQwwYmm-PDc2rZqg01FY4hpBh-XF5OHcgTVZZBes,17395
|
|
4
|
+
epstein_files/documents/email.py,sha256=rGnJjRzLnq70jZpWU3oslNW-FhztlcszS3v7mwEcmzY,41222
|
|
5
5
|
epstein_files/documents/emails/email_header.py,sha256=wkPfSLbmzkAeQwvhf0bAeFDLPbQT-EeG0v8vNNLYktM,7502
|
|
6
6
|
epstein_files/documents/imessage/text_message.py,sha256=icIiKRRuZapkV9r_PID_7hEfy7YvPrIm9Emc4QiYxbw,2806
|
|
7
7
|
epstein_files/documents/json_file.py,sha256=WcZW5NNqA67rHTdopbOGtup00muNaLlvrNgKb-K4zO8,1504
|
|
8
8
|
epstein_files/documents/messenger_log.py,sha256=zRVVe82cNmNmg50kqScH38yGGoSx8NjCWfLwFGUb0rs,6501
|
|
9
9
|
epstein_files/documents/other_file.py,sha256=CCwOYsipYTWZj8pSTl0kgUy_LRu7Z5ZuWygQEYhilNA,9778
|
|
10
|
-
epstein_files/epstein_files.py,sha256=
|
|
10
|
+
epstein_files/epstein_files.py,sha256=LsvD9O1YNp9xFIVOrswOfnbfafxgbS9ve9cqSfQENy8,17881
|
|
11
11
|
epstein_files/util/constant/common_words.py,sha256=aR0UjoWmxyR49XS-DtHECQ1CiA_bK8hNP6CQ1TS9yZA,3696
|
|
12
|
-
epstein_files/util/constant/html.py,sha256=
|
|
13
|
-
epstein_files/util/constant/names.py,sha256=
|
|
14
|
-
epstein_files/util/constant/output_files.py,sha256=
|
|
12
|
+
epstein_files/util/constant/html.py,sha256=ebBgoUAnKnOGSK-kMF-ybvc_2sUgAuJKiH1L4rx5kYY,1526
|
|
13
|
+
epstein_files/util/constant/names.py,sha256=ceFaok_1azCzTvlsE3t-urYiKu7YNc8PumwXpkE3dmc,10513
|
|
14
|
+
epstein_files/util/constant/output_files.py,sha256=et1y3AzkxKqK0k-wDhMEsQFMfoXrUpoJCn6nQONurkU,1911
|
|
15
15
|
epstein_files/util/constant/strings.py,sha256=rOKmYrGIZFPQbhCgd8z7mUF0CbQQBO4ij8kXvGTDkoE,1857
|
|
16
16
|
epstein_files/util/constant/urls.py,sha256=-cylNL7xJi18a6fawVTF8MO3oEdjMvo9Hg1MwC-7ydI,5098
|
|
17
17
|
epstein_files/util/constants.py,sha256=4WABkEBshduSV4cZkxv0MwgJci9g6MxjhtJfPjersEo,112495
|
|
18
18
|
epstein_files/util/data.py,sha256=JccGFZGiCGm7XtwpQTocIjGYOr6hTUpEPwHhjyW9Xnc,3164
|
|
19
19
|
epstein_files/util/doc_cfg.py,sha256=aBIm0hyxf-aeMsb8ZUNiQFVsPFimjVUIkrVdDrg1iQU,9105
|
|
20
|
-
epstein_files/util/env.py,sha256=
|
|
20
|
+
epstein_files/util/env.py,sha256=AWZYDHQ6JRHobY1o7BHRbVObYpBejhsUN4TrOyZbzgQ,5783
|
|
21
21
|
epstein_files/util/file_helper.py,sha256=uJzNHihuAi2-XMNbUdjdovgppcIt-fVIIrXHXVShA6Q,2904
|
|
22
|
-
epstein_files/util/highlighted_group.py,sha256=
|
|
22
|
+
epstein_files/util/highlighted_group.py,sha256=4r-asWz53zVHKckONxTOluFCRqbX92nAID1gdXqf08g,37047
|
|
23
23
|
epstein_files/util/logging.py,sha256=fuREq06xUUI3DfCV2JE-8QM-sQKxpLDj0_AYFO6qR1M,1983
|
|
24
|
-
epstein_files/util/output.py,sha256=
|
|
25
|
-
epstein_files/util/rich.py,sha256=
|
|
24
|
+
epstein_files/util/output.py,sha256=WK37ZbTvhPHczH8GNly2Pjw9IwAsiF_yn43u7Q2jf7k,10051
|
|
25
|
+
epstein_files/util/rich.py,sha256=oic1wcqipTSj9G1YYP8HTzsJvGeIgrK8vN95uGOJ8sU,15715
|
|
26
26
|
epstein_files/util/search_result.py,sha256=1fxe0KPBQXBk4dLfu6m0QXIzYfZCzvaSkWqvghJGzxY,567
|
|
27
27
|
epstein_files/util/timer.py,sha256=8hxW4Y1JcTUfnBrHh7sL2pM9xu1sL4HFQM4CmmzTarU,837
|
|
28
28
|
epstein_files/util/word_count.py,sha256=o_-HnfzHdPDPR8oA_dv6fjy1dbsHee8p_aoe62PEQHw,9213
|
|
29
|
-
epstein_files-1.0.
|
|
30
|
-
epstein_files-1.0.
|
|
31
|
-
epstein_files-1.0.
|
|
32
|
-
epstein_files-1.0.
|
|
33
|
-
epstein_files-1.0.
|
|
29
|
+
epstein_files-1.1.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
30
|
+
epstein_files-1.1.0.dist-info/METADATA,sha256=X3RI8102moyBUsT1h6TXPrNdMp6uSokA_d-g5x2GGAA,5866
|
|
31
|
+
epstein_files-1.1.0.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
|
|
32
|
+
epstein_files-1.1.0.dist-info/entry_points.txt,sha256=5qYgwAXpxegeAicD_rzda_trDRnUC51F5UVDpcZ7j6Q,240
|
|
33
|
+
epstein_files-1.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|