epstein-files 1.1.2__py3-none-any.whl → 1.1.5__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 +17 -20
- epstein_files/documents/communication.py +3 -3
- epstein_files/documents/document.py +3 -0
- epstein_files/documents/email.py +75 -64
- epstein_files/documents/imessage/text_message.py +5 -9
- epstein_files/documents/messenger_log.py +2 -2
- epstein_files/epstein_files.py +17 -15
- epstein_files/util/constant/names.py +39 -38
- epstein_files/util/constant/strings.py +1 -0
- epstein_files/util/constants.py +65 -9
- epstein_files/util/data.py +9 -1
- epstein_files/util/doc_cfg.py +8 -2
- epstein_files/util/env.py +11 -1
- epstein_files/util/file_helper.py +4 -1
- epstein_files/util/highlighted_group.py +99 -52
- epstein_files/util/output.py +112 -94
- epstein_files/util/rich.py +28 -35
- epstein_files/util/word_count.py +1 -2
- {epstein_files-1.1.2.dist-info → epstein_files-1.1.5.dist-info}/METADATA +4 -1
- epstein_files-1.1.5.dist-info/RECORD +33 -0
- epstein_files-1.1.2.dist-info/RECORD +0 -33
- {epstein_files-1.1.2.dist-info → epstein_files-1.1.5.dist-info}/LICENSE +0 -0
- {epstein_files-1.1.2.dist-info → epstein_files-1.1.5.dist-info}/WHEEL +0 -0
- {epstein_files-1.1.2.dist-info → epstein_files-1.1.5.dist-info}/entry_points.txt +0 -0
epstein_files/util/output.py
CHANGED
|
@@ -3,7 +3,7 @@ import json
|
|
|
3
3
|
from rich.padding import Padding
|
|
4
4
|
|
|
5
5
|
from epstein_files.documents.document import Document
|
|
6
|
-
from epstein_files.documents.email import
|
|
6
|
+
from epstein_files.documents.email import KRASSNER_RECIPIENTS, Email
|
|
7
7
|
from epstein_files.documents.messenger_log import MessengerLog
|
|
8
8
|
from epstein_files.documents.other_file import FIRST_FEW_LINES, OtherFile
|
|
9
9
|
from epstein_files.epstein_files import EpsteinFiles, count_by_month
|
|
@@ -11,20 +11,24 @@ from epstein_files.util.constant import output_files
|
|
|
11
11
|
from epstein_files.util.constant.html import *
|
|
12
12
|
from epstein_files.util.constant.names import *
|
|
13
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, TIMESTAMP_STYLE
|
|
14
|
+
from epstein_files.util.constant.strings import DEFAULT_NAME_STYLE, TIMESTAMP_DIM, TIMESTAMP_STYLE
|
|
15
15
|
from epstein_files.util.data import dict_sets_to_lists, sort_dict
|
|
16
16
|
from epstein_files.util.env import args
|
|
17
17
|
from epstein_files.util.file_helper import log_file_write
|
|
18
|
+
from epstein_files.util.highlighted_group import (JUNK_EMAILERS, QUESTION_MARKS_TXT, get_category_txt_for_name,
|
|
19
|
+
get_info_for_name, get_style_for_name, styled_name)
|
|
18
20
|
from epstein_files.util.logging import logger
|
|
19
21
|
from epstein_files.util.rich import *
|
|
20
22
|
|
|
23
|
+
OTHER_INTERESTING_EMAILS_SUBTITLE = 'Other Interesting Emails\n(these emails have been flagged as being of particular interest)'
|
|
21
24
|
PRINT_COLOR_KEY_EVERY_N_EMAILS = 150
|
|
25
|
+
ALT_INFO_STYLE = 'medium_purple4'
|
|
22
26
|
|
|
23
27
|
# Order matters. Default names to print emails for.
|
|
24
28
|
DEFAULT_EMAILERS = [
|
|
25
29
|
JEREMY_RUBIN,
|
|
26
|
-
JOI_ITO,
|
|
27
30
|
JABOR_Y,
|
|
31
|
+
JOI_ITO,
|
|
28
32
|
STEVEN_SINOFSKY,
|
|
29
33
|
AL_SECKEL,
|
|
30
34
|
DANIEL_SIAD,
|
|
@@ -43,6 +47,18 @@ DEFAULT_EMAILERS = [
|
|
|
43
47
|
JENNIFER_JACQUET,
|
|
44
48
|
ZUBAIR_KHAN,
|
|
45
49
|
None,
|
|
50
|
+
JEFFREY_EPSTEIN,
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
INTERESTING_EMAIL_IDS = [
|
|
54
|
+
'032229', # Michael Wolff on strategy
|
|
55
|
+
'028784', # seminars: Money / Power
|
|
56
|
+
'029342', # Hakeem Jeffries
|
|
57
|
+
'023454', # Email invitation sent to tech CEOs + Epstein
|
|
58
|
+
'030630', # 'What happens with zubair's project?'
|
|
59
|
+
'033178', # 'How is it going with Zubair?'
|
|
60
|
+
'022396', # Ukraine friend
|
|
61
|
+
# '023627', # Michael Wolff article (already printed bc epstein->epstein email)
|
|
46
62
|
]
|
|
47
63
|
|
|
48
64
|
INVALID_FOR_EPSTEIN_WEB = JUNK_EMAILERS + KRASSNER_RECIPIENTS + [
|
|
@@ -53,6 +69,19 @@ INVALID_FOR_EPSTEIN_WEB = JUNK_EMAILERS + KRASSNER_RECIPIENTS + [
|
|
|
53
69
|
]
|
|
54
70
|
|
|
55
71
|
|
|
72
|
+
def print_email_timeline(epstein_files: EpsteinFiles) -> None:
|
|
73
|
+
"""Print a table of all emails in chronological order."""
|
|
74
|
+
emails = Document.sort_by_timestamp([e for e in epstein_files.non_duplicate_emails() if not e.is_junk_mail()])
|
|
75
|
+
title = f'Table of All {len(emails):,} Non-Junk Emails in Chronological Order (actual emails below)'
|
|
76
|
+
table = Email.build_emails_table(emails, title=title, show_length=True)
|
|
77
|
+
console.print(Padding(table, (2, 0)))
|
|
78
|
+
print_subtitle_panel('The Chronologically Ordered Emails')
|
|
79
|
+
console.line()
|
|
80
|
+
|
|
81
|
+
for email in emails:
|
|
82
|
+
console.print(email)
|
|
83
|
+
|
|
84
|
+
|
|
56
85
|
def print_emails_section(epstein_files: EpsteinFiles) -> list[Email]:
|
|
57
86
|
"""Returns emails that were printed (may contain dupes if printed for both author and recipient)."""
|
|
58
87
|
print_section_header(('Selections from ' if not args.all_emails else '') + 'His Emails')
|
|
@@ -70,7 +99,7 @@ def print_emails_section(epstein_files: EpsteinFiles) -> list[Email]:
|
|
|
70
99
|
|
|
71
100
|
print_other_page_link(epstein_files)
|
|
72
101
|
console.line(2)
|
|
73
|
-
console.print(
|
|
102
|
+
console.print(_table_of_selected_emailers(emailers_to_print, epstein_files))
|
|
74
103
|
console.print(Padding(_all_emailers_table(epstein_files), (2, 0)))
|
|
75
104
|
|
|
76
105
|
for author in emailers_to_print:
|
|
@@ -83,8 +112,19 @@ def print_emails_section(epstein_files: EpsteinFiles) -> list[Email]:
|
|
|
83
112
|
print_color_key()
|
|
84
113
|
num_emails_printed_since_last_color_key = 0
|
|
85
114
|
|
|
86
|
-
if
|
|
87
|
-
|
|
115
|
+
if args.names:
|
|
116
|
+
return already_printed_emails
|
|
117
|
+
|
|
118
|
+
# Print other interesting emails
|
|
119
|
+
already_printed_ids = [email.file_id for email in already_printed_emails]
|
|
120
|
+
extra_emails = [e for e in epstein_files.for_ids(INTERESTING_EMAIL_IDS) if e.file_id not in already_printed_ids]
|
|
121
|
+
print_subtitle_panel(OTHER_INTERESTING_EMAILS_SUBTITLE)
|
|
122
|
+
console.line()
|
|
123
|
+
|
|
124
|
+
for other_email in extra_emails:
|
|
125
|
+
console.print(other_email)
|
|
126
|
+
|
|
127
|
+
epstein_files.print_email_device_info()
|
|
88
128
|
|
|
89
129
|
if args.all_emails:
|
|
90
130
|
_verify_all_emails_were_printed(epstein_files, already_printed_emails)
|
|
@@ -96,8 +136,9 @@ def print_emails_section(epstein_files: EpsteinFiles) -> list[Email]:
|
|
|
96
136
|
|
|
97
137
|
|
|
98
138
|
def print_json_files(epstein_files: EpsteinFiles):
|
|
139
|
+
"""Print all the JsonFile objects"""
|
|
99
140
|
if args.build:
|
|
100
|
-
json_data = {
|
|
141
|
+
json_data = {jf.url_slug: jf.json_data() for jf in epstein_files.json_files}
|
|
101
142
|
|
|
102
143
|
with open(JSON_FILES_JSON_PATH, 'w') as f:
|
|
103
144
|
f.write(json.dumps(json_data, sort_keys=True))
|
|
@@ -109,6 +150,17 @@ def print_json_files(epstein_files: EpsteinFiles):
|
|
|
109
150
|
console.print_json(json_file.json_str(), indent=4, sort_keys=False)
|
|
110
151
|
|
|
111
152
|
|
|
153
|
+
def print_json_metadata(epstein_files: EpsteinFiles) -> None:
|
|
154
|
+
json_str = epstein_files.json_metadata()
|
|
155
|
+
|
|
156
|
+
if args.build:
|
|
157
|
+
with open(JSON_METADATA_PATH, 'w') as f:
|
|
158
|
+
f.write(json_str)
|
|
159
|
+
log_file_write(JSON_METADATA_PATH)
|
|
160
|
+
else:
|
|
161
|
+
console.print_json(json_str, indent=4, sort_keys=True)
|
|
162
|
+
|
|
163
|
+
|
|
112
164
|
def print_json_stats(epstein_files: EpsteinFiles) -> None:
|
|
113
165
|
console.line(5)
|
|
114
166
|
console.print(Panel('JSON Stats Dump', expand=True, style='reverse bold'), '\n')
|
|
@@ -152,91 +204,6 @@ def print_text_messages_section(imessage_logs: list[MessengerLog]) -> None:
|
|
|
152
204
|
console.line(2)
|
|
153
205
|
|
|
154
206
|
|
|
155
|
-
def table_of_selected_emailers(_list: list[str | None], epstein_files: EpsteinFiles) -> Table:
|
|
156
|
-
"""Add the first emailed_at timestamp for each emailer if 'epstein_files' provided."""
|
|
157
|
-
header_pfx = '' if args.all_emails else 'Selected '
|
|
158
|
-
table = build_table(f'{header_pfx}Email Conversations Grouped by Counterparty Will Appear in this Order')
|
|
159
|
-
table.add_column('Start Date')
|
|
160
|
-
table.add_column('Name', max_width=25, no_wrap=True)
|
|
161
|
-
table.add_column('Category', justify='center', style='dim italic')
|
|
162
|
-
table.add_column('Num', justify='right', style='wheat4')
|
|
163
|
-
table.add_column('Info', style='white italic')
|
|
164
|
-
current_year = 1990
|
|
165
|
-
current_year_month = current_year * 12
|
|
166
|
-
grey_idx = 0
|
|
167
|
-
|
|
168
|
-
for i, name in enumerate(_list):
|
|
169
|
-
earliest_email_date = (epstein_files.earliest_email_at(name) or FALLBACK_TIMESTAMP).date()
|
|
170
|
-
year_months = (earliest_email_date.year * 12) + earliest_email_date.month
|
|
171
|
-
|
|
172
|
-
# Color year rollovers more brightly
|
|
173
|
-
if current_year != earliest_email_date.year:
|
|
174
|
-
grey_idx = 0
|
|
175
|
-
elif current_year_month != year_months:
|
|
176
|
-
grey_idx = ((current_year_month - 1) % 12) + 1
|
|
177
|
-
|
|
178
|
-
current_year_month = year_months
|
|
179
|
-
current_year = earliest_email_date.year
|
|
180
|
-
category = get_category_for_name(name)
|
|
181
|
-
info = get_info_for_name(name)
|
|
182
|
-
|
|
183
|
-
if category and category.plain == 'paula_heil_fisher': # TODO: hacky
|
|
184
|
-
category = None
|
|
185
|
-
elif category and info:
|
|
186
|
-
info = info.removeprefix(f"{category.plain}, ")
|
|
187
|
-
elif not name:
|
|
188
|
-
info = Text('(emails whose author or recipient could not be determined)', style='medium_purple4')
|
|
189
|
-
|
|
190
|
-
table.add_row(
|
|
191
|
-
Text(str(earliest_email_date), style=f"grey{GREY_NUMBERS[grey_idx]}"),
|
|
192
|
-
Text(name or UNKNOWN, style=get_style_for_name(name or UNKNOWN, default_style='dim')),
|
|
193
|
-
category,
|
|
194
|
-
f"{len(epstein_files.emails_for(name)):,}",
|
|
195
|
-
info or '',
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
return table
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
def write_complete_emails_timeline(epstein_files: EpsteinFiles) -> None:
|
|
202
|
-
"""Print a table of all emails in chronological order."""
|
|
203
|
-
emails = [email for email in epstein_files.non_duplicate_emails() if not email.is_junk_mail()]
|
|
204
|
-
table = build_table(f'All {len(emails):,} Non-Junk Emails in Chronological Order', highlight=True)
|
|
205
|
-
table.add_column('ID', style=TIMESTAMP_DIM)
|
|
206
|
-
table.add_column('Sent At', style='dim')
|
|
207
|
-
table.add_column('Author', max_width=20)
|
|
208
|
-
table.add_column('Recipients', max_width=22)
|
|
209
|
-
table.add_column('Length', justify='right', style='wheat4')
|
|
210
|
-
table.add_column('Subject')
|
|
211
|
-
|
|
212
|
-
for email in Document.sort_by_timestamp(emails):
|
|
213
|
-
if email.is_junk_mail():
|
|
214
|
-
continue
|
|
215
|
-
|
|
216
|
-
table.add_row(
|
|
217
|
-
email.epstein_media_link(link_txt=email.source_file_id()),
|
|
218
|
-
email.timestamp_without_seconds(),
|
|
219
|
-
email.author_txt(),
|
|
220
|
-
email.recipients_txt(max_full_names=1),
|
|
221
|
-
f"{email.length()}",
|
|
222
|
-
email.subject(),
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
console.line(2)
|
|
226
|
-
console.print(table)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
def write_json_metadata(epstein_files: EpsteinFiles) -> None:
|
|
230
|
-
json_str = epstein_files.json_metadata()
|
|
231
|
-
|
|
232
|
-
if args.build:
|
|
233
|
-
with open(JSON_METADATA_PATH, 'w') as f:
|
|
234
|
-
f.write(json_str)
|
|
235
|
-
log_file_write(JSON_METADATA_PATH)
|
|
236
|
-
else:
|
|
237
|
-
console.print_json(json_str, indent=4, sort_keys=True)
|
|
238
|
-
|
|
239
|
-
|
|
240
207
|
def write_urls() -> None:
|
|
241
208
|
"""Write _URL style constant variables to URLS_ENV file so bash scripts can load as env vars."""
|
|
242
209
|
url_vars = {k: v for k, v in vars(output_files).items() if k.endswith('URL') and not k.startswith('GH')}
|
|
@@ -263,7 +230,7 @@ def _all_emailers_table(epstein_files: EpsteinFiles) -> Table:
|
|
|
263
230
|
attributed_emails = [e for e in epstein_files.non_duplicate_emails() if e.author]
|
|
264
231
|
footer = f"(identified {len(epstein_files.email_author_counts)} authors of {len(attributed_emails):,}"
|
|
265
232
|
footer = f"{footer} out of {len(epstein_files.non_duplicate_emails()):,} emails)"
|
|
266
|
-
counts_table = build_table("
|
|
233
|
+
counts_table = build_table("Everyone Who Sent or Received an Email in the Files", caption=footer)
|
|
267
234
|
|
|
268
235
|
add_cols_to_table(counts_table, [
|
|
269
236
|
'Name',
|
|
@@ -281,7 +248,7 @@ def _all_emailers_table(epstein_files: EpsteinFiles) -> Table:
|
|
|
281
248
|
|
|
282
249
|
emailer_counts = {
|
|
283
250
|
emailer: epstein_files.email_author_counts[emailer] + epstein_files.email_recipient_counts[emailer]
|
|
284
|
-
for emailer in epstein_files.all_emailers(True)
|
|
251
|
+
for emailer in epstein_files.all_emailers(include_useless=True)
|
|
285
252
|
}
|
|
286
253
|
|
|
287
254
|
for name, count in sort_dict(emailer_counts):
|
|
@@ -317,6 +284,57 @@ def _is_ok_for_epstein_web(name: str | None) -> bool:
|
|
|
317
284
|
return True
|
|
318
285
|
|
|
319
286
|
|
|
287
|
+
def _table_of_selected_emailers(_list: list[str | None], epstein_files: EpsteinFiles) -> Table:
|
|
288
|
+
"""Add the first emailed_at timestamp for each emailer if 'epstein_files' provided."""
|
|
289
|
+
header_pfx = '' if args.all_emails else 'Selected '
|
|
290
|
+
table = build_table(f'{header_pfx}Email Conversations Grouped by Counterparty Will Appear in this Order')
|
|
291
|
+
table.add_column('Start Date')
|
|
292
|
+
table.add_column('Name', max_width=25, no_wrap=True)
|
|
293
|
+
table.add_column('Category', justify='center', style='dim italic')
|
|
294
|
+
table.add_column('Num', justify='right', style='wheat4')
|
|
295
|
+
table.add_column('Info', style='white italic')
|
|
296
|
+
current_year = 1990
|
|
297
|
+
current_year_month = current_year * 12
|
|
298
|
+
grey_idx = 0
|
|
299
|
+
|
|
300
|
+
for i, name in enumerate(_list):
|
|
301
|
+
earliest_email_date = (epstein_files.earliest_email_at(name)).date()
|
|
302
|
+
year_months = (earliest_email_date.year * 12) + earliest_email_date.month
|
|
303
|
+
|
|
304
|
+
# Color year rollovers more brightly
|
|
305
|
+
if current_year != earliest_email_date.year:
|
|
306
|
+
grey_idx = 0
|
|
307
|
+
elif current_year_month != year_months:
|
|
308
|
+
grey_idx = ((current_year_month - 1) % 12) + 1
|
|
309
|
+
|
|
310
|
+
current_year_month = year_months
|
|
311
|
+
current_year = earliest_email_date.year
|
|
312
|
+
category = get_category_txt_for_name(name)
|
|
313
|
+
info = get_info_for_name(name)
|
|
314
|
+
style = get_style_for_name(name, default_style='none')
|
|
315
|
+
|
|
316
|
+
if name == JEFFREY_EPSTEIN:
|
|
317
|
+
info = Text('(emails sent by Epstein to himself that would not otherwise be printed)', style=ALT_INFO_STYLE)
|
|
318
|
+
if category and category.plain == 'paula': # TODO: hacky
|
|
319
|
+
category = None
|
|
320
|
+
elif category and info:
|
|
321
|
+
info = info.removeprefix(f"{category.plain}, ").removeprefix(category.plain)
|
|
322
|
+
elif not name:
|
|
323
|
+
info = Text('(emails whose author or recipient could not be determined)', style=ALT_INFO_STYLE)
|
|
324
|
+
elif style == 'none' and '@' not in name and not (category or info):
|
|
325
|
+
info = QUESTION_MARKS_TXT
|
|
326
|
+
|
|
327
|
+
table.add_row(
|
|
328
|
+
Text(str(earliest_email_date), style=f"grey{GREY_NUMBERS[grey_idx]}"),
|
|
329
|
+
styled_name(name, default_style='dim'),
|
|
330
|
+
category,
|
|
331
|
+
f"{len(epstein_files.emails_for(name)):,}",
|
|
332
|
+
info or '',
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
return table
|
|
336
|
+
|
|
337
|
+
|
|
320
338
|
def _verify_all_emails_were_printed(epstein_files: EpsteinFiles, already_printed_emails: list[Email]) -> None:
|
|
321
339
|
"""Log warnings if some emails were never printed."""
|
|
322
340
|
email_ids_that_were_printed = set([email.file_id for email in already_printed_emails])
|
epstein_files/util/rich.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# Rich reference: https://rich.readthedocs.io/en/latest/reference.html
|
|
2
2
|
import json
|
|
3
|
+
from copy import deepcopy
|
|
3
4
|
from os import devnull
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
from rich.align import Align
|
|
7
|
-
from rich.console import Console, RenderableType
|
|
8
|
+
from rich.console import Console, Group, RenderableType
|
|
8
9
|
from rich.markup import escape
|
|
9
10
|
from rich.panel import Panel
|
|
10
11
|
from rich.padding import Padding
|
|
@@ -14,23 +15,23 @@ from rich.theme import Theme
|
|
|
14
15
|
|
|
15
16
|
from epstein_files.util.constant.html import CONSOLE_HTML_FORMAT, HTML_TERMINAL_THEME, PAGE_TITLE
|
|
16
17
|
from epstein_files.util.constant.names import UNKNOWN
|
|
17
|
-
from epstein_files.util.constant.strings import DEFAULT, EMAIL, NA,
|
|
18
|
+
from epstein_files.util.constant.strings import DEFAULT, EMAIL, NA, TEXT_MESSAGE
|
|
18
19
|
from epstein_files.util.constant.urls import *
|
|
19
|
-
from epstein_files.util.constants import
|
|
20
|
-
from epstein_files.util.data import json_safe
|
|
20
|
+
from epstein_files.util.constants import HEADER_ABBREVIATIONS
|
|
21
|
+
from epstein_files.util.data import json_safe, without_falsey
|
|
21
22
|
from epstein_files.util.env import args
|
|
22
23
|
from epstein_files.util.file_helper import log_file_write
|
|
23
|
-
from epstein_files.util.highlighted_group import
|
|
24
|
-
get_category_for_name, get_info_for_name, get_style_for_name)
|
|
24
|
+
from epstein_files.util.highlighted_group import ALL_HIGHLIGHTS, HIGHLIGHTED_NAMES, EpsteinHighlighter
|
|
25
25
|
from epstein_files.util.logging import logger
|
|
26
26
|
|
|
27
27
|
TITLE_WIDTH = 50
|
|
28
|
+
SUBTITLE_WIDTH = 110
|
|
28
29
|
MIN_AUTHOR_PANEL_WIDTH = 80
|
|
29
30
|
NUM_COLOR_KEY_COLS = 4
|
|
30
31
|
NA_TXT = Text(NA, style='dim')
|
|
32
|
+
SUBTITLE_PADDING = (2, 0, 1, 0)
|
|
31
33
|
GREY_NUMBERS = [58, 39, 39, 35, 30, 27, 23, 23, 19, 19, 15, 15, 15]
|
|
32
34
|
|
|
33
|
-
DEFAULT_NAME_STYLE = 'gray46'
|
|
34
35
|
INFO_STYLE = 'white dim italic'
|
|
35
36
|
KEY_STYLE = 'honeydew2 bold'
|
|
36
37
|
LAST_TIMESTAMP_STYLE = 'wheat4'
|
|
@@ -89,17 +90,14 @@ def add_cols_to_table(table: Table, col_names: list[str | dict]) -> None:
|
|
|
89
90
|
|
|
90
91
|
if isinstance(col, dict):
|
|
91
92
|
col_name = col['name']
|
|
92
|
-
kwargs = col
|
|
93
|
+
kwargs = deepcopy(col)
|
|
94
|
+
kwargs['justify'] = kwargs.get('justify', justify)
|
|
93
95
|
del kwargs['name']
|
|
94
|
-
|
|
95
|
-
if 'justify' in col:
|
|
96
|
-
justify = col['justify']
|
|
97
|
-
del col['justify']
|
|
98
96
|
else:
|
|
99
97
|
col_name = col
|
|
100
|
-
kwargs = {}
|
|
98
|
+
kwargs = {'justify': justify}
|
|
101
99
|
|
|
102
|
-
table.add_column(col_name,
|
|
100
|
+
table.add_column(col_name, **kwargs)
|
|
103
101
|
|
|
104
102
|
|
|
105
103
|
def build_highlighter(pattern: str) -> EpsteinHighlighter:
|
|
@@ -151,17 +149,17 @@ def parenthesize(msg: str | Text, style: str = '') -> Text:
|
|
|
151
149
|
return Text('(', style=style).append(txt).append(')')
|
|
152
150
|
|
|
153
151
|
|
|
154
|
-
def print_author_panel(msg: str,
|
|
152
|
+
def print_author_panel(msg: str, footer: str | None, style: str | None) -> None:
|
|
155
153
|
"""Print a panel with the name of an emailer and a few tidbits of information about them."""
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
154
|
+
style = 'white' if (not style or style == DEFAULT) else style
|
|
155
|
+
panel_style = f"black on {style} bold"
|
|
156
|
+
width = max(MIN_AUTHOR_PANEL_WIDTH, len(msg) + 4, len(footer or '') + 8)
|
|
157
|
+
elements: list[RenderableType] = [Panel(Text(msg, justify='center'), width=width, style=panel_style)]
|
|
160
158
|
|
|
161
159
|
if footer:
|
|
162
|
-
|
|
160
|
+
elements.append(Text(f"({footer})", justify='center', style=f"{style} italic"))
|
|
163
161
|
|
|
164
|
-
|
|
162
|
+
print_centered(Padding(Group(*elements), (2, 0, 1, 0)))
|
|
165
163
|
|
|
166
164
|
|
|
167
165
|
def print_centered(obj: RenderableType, style: str = '') -> None:
|
|
@@ -188,7 +186,8 @@ def print_color_key() -> None:
|
|
|
188
186
|
print_centered(vertically_pad(color_table))
|
|
189
187
|
|
|
190
188
|
|
|
191
|
-
def print_title_page_header(
|
|
189
|
+
def print_title_page_header() -> None:
|
|
190
|
+
"""Top half of the title page."""
|
|
192
191
|
print_page_title(width=TITLE_WIDTH)
|
|
193
192
|
site_type = EMAIL if (args.all_emails or args.email_timeline) else TEXT_MESSAGE
|
|
194
193
|
title = f"This is the " + ('chronological ' if args.email_timeline else '') + f"Epstein {site_type.title()}s Page"
|
|
@@ -209,6 +208,7 @@ def print_title_page_header(epstein_files: 'EpsteinFiles') -> None:
|
|
|
209
208
|
|
|
210
209
|
|
|
211
210
|
def print_title_page_tables(epstein_files: 'EpsteinFiles') -> None:
|
|
211
|
+
"""Bottom half of the title page."""
|
|
212
212
|
_print_external_links()
|
|
213
213
|
console.line()
|
|
214
214
|
_print_abbreviations_table()
|
|
@@ -246,7 +246,7 @@ def print_other_page_link(epstein_files: 'EpsteinFiles') -> None:
|
|
|
246
246
|
print_centered(parenthesize(txt), style=OTHER_PAGE_MSG_STYLE)
|
|
247
247
|
chrono_emails_markup = link_text_obj(CHRONOLOGICAL_EMAILS_URL, 'a page', style='light_slate_grey bold')
|
|
248
248
|
chrono_emails_txt = Text(f"there's also ").append(chrono_emails_markup)
|
|
249
|
-
chrono_emails_txt.append(' with
|
|
249
|
+
chrono_emails_txt.append(' with all the emails in chronological order')
|
|
250
250
|
print_centered(parenthesize(chrono_emails_txt), style=OTHER_PAGE_MSG_STYLE)
|
|
251
251
|
|
|
252
252
|
|
|
@@ -259,16 +259,9 @@ def print_page_title(expand: bool = True, width: int | None = None) -> None:
|
|
|
259
259
|
console.line(2)
|
|
260
260
|
|
|
261
261
|
|
|
262
|
-
def print_subtitle_panel(msg: str, style: str = 'black on white'
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
actual_padding: tuple[int, int, int, int] = tuple(_padding)
|
|
266
|
-
panel = Panel(Text.from_markup(msg, justify='center'), width=70, style=style)
|
|
267
|
-
|
|
268
|
-
if centered:
|
|
269
|
-
console.print(Align.center(Padding(panel, actual_padding)))
|
|
270
|
-
else:
|
|
271
|
-
console.print(Padding(panel, actual_padding))
|
|
262
|
+
def print_subtitle_panel(msg: str, style: str = 'black on white') -> None:
|
|
263
|
+
panel = Panel(Text.from_markup(msg, justify='center'), width=SUBTITLE_WIDTH, style=style)
|
|
264
|
+
print_centered(Padding(panel, SUBTITLE_PADDING))
|
|
272
265
|
|
|
273
266
|
|
|
274
267
|
def print_section_header(msg: str, style: str = SECTION_HEADER_STYLE, is_centered: bool = False) -> None:
|
|
@@ -306,8 +299,8 @@ def wrap_in_markup_style(msg: str, style: str | None = None) -> str:
|
|
|
306
299
|
return msg
|
|
307
300
|
|
|
308
301
|
|
|
309
|
-
def write_html(output_path: Path) -> None:
|
|
310
|
-
if not
|
|
302
|
+
def write_html(output_path: Path | None) -> None:
|
|
303
|
+
if not output_path:
|
|
311
304
|
logger.warning(f"Not writing HTML because args.build={args.build}.")
|
|
312
305
|
return
|
|
313
306
|
|
epstein_files/util/word_count.py
CHANGED
|
@@ -237,8 +237,7 @@ def write_word_counts_html() -> None:
|
|
|
237
237
|
print_color_key()
|
|
238
238
|
console.line()
|
|
239
239
|
console.print(word_count)
|
|
240
|
-
|
|
241
|
-
print_subtitle_panel(f"{len(COMMON_WORDS_LIST):,} Excluded Words", centered=True)
|
|
240
|
+
print_subtitle_panel(f"{len(COMMON_WORDS_LIST):,} Excluded Words")
|
|
242
241
|
console.print(', '.join(COMMON_WORDS_LIST), highlight=False)
|
|
243
242
|
write_html(WORD_COUNT_HTML_PATH)
|
|
244
243
|
timer.print_at_checkpoint(f"Finished counting words")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: epstein-files
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.5
|
|
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
|
|
@@ -81,6 +81,9 @@ epstein_diff 030999 020442
|
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
The first time you run anything it will take a few minutes to fix all the janky OCR text, attribute the redacted emails, etc. After that things will be quick.
|
|
84
|
+
|
|
85
|
+
The commands used to build the various sites that are deployed on Github Pages can be found in [`deploy.sh`](./deploy.sh).
|
|
86
|
+
|
|
84
87
|
Run `epstein_generate --help` for command line option assistance.
|
|
85
88
|
|
|
86
89
|
**Optional:** There are a handful of emails that I extracted from the legal filings they were contained in. If you want to include these files in your local analysis you'll need to copy those files from the repo into your local document directory. Something like:
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
epstein_files/__init__.py,sha256=mp1fugQzZ_5RwSbs8JSxx_17hC8W9YL0I9j5J9LS7Jw,5278
|
|
2
|
+
epstein_files/documents/communication.py,sha256=NzcZ3vQBjVAovasnxpUyII4weycMaJ2T3fc_8d4eg-U,1875
|
|
3
|
+
epstein_files/documents/document.py,sha256=z8l65DvgCMrUbVpnhfYUPYtLBqlDN8l-88zZQWWOxAE,17527
|
|
4
|
+
epstein_files/documents/email.py,sha256=pVoW-w9QfvJDI4DeGAxyywXYHf4VP6hKe5Ly16QtOLA,43928
|
|
5
|
+
epstein_files/documents/emails/email_header.py,sha256=wkPfSLbmzkAeQwvhf0bAeFDLPbQT-EeG0v8vNNLYktM,7502
|
|
6
|
+
epstein_files/documents/imessage/text_message.py,sha256=pflCbV4qamJa0ueJG60ifbk0xeUJrfFzf-NVIGJLcuU,3353
|
|
7
|
+
epstein_files/documents/json_file.py,sha256=WcZW5NNqA67rHTdopbOGtup00muNaLlvrNgKb-K4zO8,1504
|
|
8
|
+
epstein_files/documents/messenger_log.py,sha256=8EVNnyTKuUeeso9R5vXg_sJf2Ont33e66rMvbzX40Rc,7402
|
|
9
|
+
epstein_files/documents/other_file.py,sha256=TeEzsfGN_mTFZPhfyt9ihxK9oTCYwI8sRLplTsgpOMY,9893
|
|
10
|
+
epstein_files/epstein_files.py,sha256=LdaDMJzUPQuiLNTpsGB3PbojyjVTwMJehjypKyN8V2I,15331
|
|
11
|
+
epstein_files/util/constant/common_words.py,sha256=C1JERPnOGHV2UMC71aEue1i9QTQog-RfT3IzdcYQOYQ,3702
|
|
12
|
+
epstein_files/util/constant/html.py,sha256=MFooFV8KfFBCm9hL1u6A3hi_u37i7lL6UKAYoKQj3PI,1505
|
|
13
|
+
epstein_files/util/constant/names.py,sha256=7rnBq86jaqyDJtpa-nw6zQ029kfLeMhKto66kjkGeSg,10412
|
|
14
|
+
epstein_files/util/constant/output_files.py,sha256=et1y3AzkxKqK0k-wDhMEsQFMfoXrUpoJCn6nQONurkU,1911
|
|
15
|
+
epstein_files/util/constant/strings.py,sha256=05OFmCyxXuH9TwemT9TZNyHmvYkbi5iH9E5nqBC4C0c,1991
|
|
16
|
+
epstein_files/util/constant/urls.py,sha256=VqgqxC2IbX90yw9kfGTwAwc7VVo46TCgDVrkPy3adV4,5127
|
|
17
|
+
epstein_files/util/constants.py,sha256=cF0D3GnpNZUuJiNZjU2qxuNoIzyMq_tDYHcvgYvoTQk,118756
|
|
18
|
+
epstein_files/util/data.py,sha256=XVPXb3qjjv5CdpORvvJjecZqxI7DdhMCQesu-4lqN0Q,3413
|
|
19
|
+
epstein_files/util/doc_cfg.py,sha256=6ErYIgDazH0WbJ27VvMwNrkKytlYAbxHqq1n_surxus,9338
|
|
20
|
+
epstein_files/util/env.py,sha256=5ZIwBUlIg37HBq_Z_DtkhXNDxPtnxOtJ_HD7ek3IoQI,6555
|
|
21
|
+
epstein_files/util/file_helper.py,sha256=MpG1hI7DGs05fV9KSVb_ltc98DC8tv1E_TTo5X_E7Js,3010
|
|
22
|
+
epstein_files/util/highlighted_group.py,sha256=YryxT0uJyOlcrGjgERaCOvlZwPRXieaniv8h_yhCcoE,54339
|
|
23
|
+
epstein_files/util/logging.py,sha256=F45YqEKAiIb0rDZnOB7XuaY-dOkOKrsfSzO1VVqY508,2097
|
|
24
|
+
epstein_files/util/output.py,sha256=5hPiYVR27azXfKQoqEAmXy9CvinUklKvsP6m3DCnsCE,14800
|
|
25
|
+
epstein_files/util/rich.py,sha256=J7IaXXF2duuajzwvCPMIhDZZfL1y63c8rQ00lumYwAs,14347
|
|
26
|
+
epstein_files/util/search_result.py,sha256=1fxe0KPBQXBk4dLfu6m0QXIzYfZCzvaSkWqvghJGzxY,567
|
|
27
|
+
epstein_files/util/timer.py,sha256=QqqXAQofKPWkDngNwG0mOqRn7nHcAR-BGQjqAwZfXoE,840
|
|
28
|
+
epstein_files/util/word_count.py,sha256=rTAo-C_I8O_y2tfUfVCYAMqXgSe1oET0TxGKepYdtiE,9199
|
|
29
|
+
epstein_files-1.1.5.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
30
|
+
epstein_files-1.1.5.dist-info/METADATA,sha256=pLpbillvKcnF10ch839Eq0sxsi8u30T7_9k49WEHW18,5991
|
|
31
|
+
epstein_files-1.1.5.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
|
|
32
|
+
epstein_files-1.1.5.dist-info/entry_points.txt,sha256=5qYgwAXpxegeAicD_rzda_trDRnUC51F5UVDpcZ7j6Q,240
|
|
33
|
+
epstein_files-1.1.5.dist-info/RECORD,,
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
epstein_files/__init__.py,sha256=dPSYqe6CZyXFJviCsrVLPy-uxJxG5xS_W9ZDjUih5f0,5484
|
|
2
|
-
epstein_files/documents/communication.py,sha256=4xcmCg4108D4Rln4tiXbm5pRBRfBGpMxcCORUCMnT6k,1908
|
|
3
|
-
epstein_files/documents/document.py,sha256=eQ0IgOUZiKz-KFgUwkWariQ5HqyM2VY3ZCJC4qRsnDg,17401
|
|
4
|
-
epstein_files/documents/email.py,sha256=b5klJTrqTydaSupC5fJkV64nDxulkWE1DAvXF8FaLCY,43369
|
|
5
|
-
epstein_files/documents/emails/email_header.py,sha256=wkPfSLbmzkAeQwvhf0bAeFDLPbQT-EeG0v8vNNLYktM,7502
|
|
6
|
-
epstein_files/documents/imessage/text_message.py,sha256=w_U2bNIKtH7rMSNP4Q0BoTDrQZ6HE2IUSFjy6rBxrgY,3348
|
|
7
|
-
epstein_files/documents/json_file.py,sha256=WcZW5NNqA67rHTdopbOGtup00muNaLlvrNgKb-K4zO8,1504
|
|
8
|
-
epstein_files/documents/messenger_log.py,sha256=pAHH8FntEyQCwoVFI3B5utSqS5LKhQrj5UD1hl3pnbg,7419
|
|
9
|
-
epstein_files/documents/other_file.py,sha256=TeEzsfGN_mTFZPhfyt9ihxK9oTCYwI8sRLplTsgpOMY,9893
|
|
10
|
-
epstein_files/epstein_files.py,sha256=K6hgDlwHFNbhdIQcc5RJN_-g_xLhNLQ1yelpzCZrZBw,15210
|
|
11
|
-
epstein_files/util/constant/common_words.py,sha256=C1JERPnOGHV2UMC71aEue1i9QTQog-RfT3IzdcYQOYQ,3702
|
|
12
|
-
epstein_files/util/constant/html.py,sha256=MFooFV8KfFBCm9hL1u6A3hi_u37i7lL6UKAYoKQj3PI,1505
|
|
13
|
-
epstein_files/util/constant/names.py,sha256=yHZ46IcrzOOEr6mdTllxaG_YvLrdhmJlWCwIGv4FaiU,10738
|
|
14
|
-
epstein_files/util/constant/output_files.py,sha256=et1y3AzkxKqK0k-wDhMEsQFMfoXrUpoJCn6nQONurkU,1911
|
|
15
|
-
epstein_files/util/constant/strings.py,sha256=2TLP_TWgErsXb9lF8k0lZVAAQ8QAc5ytKQi3PD9CAzY,1961
|
|
16
|
-
epstein_files/util/constant/urls.py,sha256=VqgqxC2IbX90yw9kfGTwAwc7VVo46TCgDVrkPy3adV4,5127
|
|
17
|
-
epstein_files/util/constants.py,sha256=ZllrMB7Imlp4ihviGOMxB5qU_hnbS7rP3PnjS0fVbAI,112742
|
|
18
|
-
epstein_files/util/data.py,sha256=JccGFZGiCGm7XtwpQTocIjGYOr6hTUpEPwHhjyW9Xnc,3164
|
|
19
|
-
epstein_files/util/doc_cfg.py,sha256=aBIm0hyxf-aeMsb8ZUNiQFVsPFimjVUIkrVdDrg1iQU,9105
|
|
20
|
-
epstein_files/util/env.py,sha256=MbS-wD0iMJyg6u-adxVGvTlXY4-ubzIjG20ovz1EMHU,6147
|
|
21
|
-
epstein_files/util/file_helper.py,sha256=PGPqXmt4Oz4bE45ybvaCZfI0w_PGKirTsrv7xw86gmY,2903
|
|
22
|
-
epstein_files/util/highlighted_group.py,sha256=Pt34bfLjkdl7VBEogeJoofXhJC9fTPHYCgb4NNDlPpI,52422
|
|
23
|
-
epstein_files/util/logging.py,sha256=F45YqEKAiIb0rDZnOB7XuaY-dOkOKrsfSzO1VVqY508,2097
|
|
24
|
-
epstein_files/util/output.py,sha256=IXBiCQYy6Z2BUGDCLeQUfJ1NUzTItjvgg-k0i4KpOzA,13751
|
|
25
|
-
epstein_files/util/rich.py,sha256=0hyzML8yCSF6FfjQIt7aJyEqG8unQhDOQzfd7e6FTAI,14599
|
|
26
|
-
epstein_files/util/search_result.py,sha256=1fxe0KPBQXBk4dLfu6m0QXIzYfZCzvaSkWqvghJGzxY,567
|
|
27
|
-
epstein_files/util/timer.py,sha256=QqqXAQofKPWkDngNwG0mOqRn7nHcAR-BGQjqAwZfXoE,840
|
|
28
|
-
epstein_files/util/word_count.py,sha256=J6aZkodXwowf09GykLgJuqwSRzrMjvefgKiM8S-T9LA,9234
|
|
29
|
-
epstein_files-1.1.2.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
30
|
-
epstein_files-1.1.2.dist-info/METADATA,sha256=clg2flS9u3aeaYxXqlwvnemMyDKBHuuMvUgiJj8d4qs,5866
|
|
31
|
-
epstein_files-1.1.2.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
|
|
32
|
-
epstein_files-1.1.2.dist-info/entry_points.txt,sha256=5qYgwAXpxegeAicD_rzda_trDRnUC51F5UVDpcZ7j6Q,240
|
|
33
|
-
epstein_files-1.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|