epstein-files 1.1.5__py3-none-any.whl → 1.2.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 +5 -1
- epstein_files/documents/document.py +7 -3
- epstein_files/documents/email.py +43 -65
- epstein_files/documents/emails/email_header.py +4 -2
- epstein_files/documents/imessage/text_message.py +3 -3
- epstein_files/documents/messenger_log.py +7 -7
- epstein_files/epstein_files.py +117 -115
- epstein_files/person.py +350 -0
- epstein_files/util/constant/names.py +35 -11
- epstein_files/util/constant/output_files.py +1 -0
- epstein_files/util/constant/strings.py +3 -2
- epstein_files/util/constant/urls.py +14 -2
- epstein_files/util/constants.py +72 -20
- epstein_files/util/data.py +0 -19
- epstein_files/util/doc_cfg.py +24 -14
- epstein_files/util/env.py +3 -1
- epstein_files/util/highlighted_group.py +154 -127
- epstein_files/util/output.py +84 -152
- epstein_files/util/rich.py +6 -21
- epstein_files/util/word_count.py +1 -1
- {epstein_files-1.1.5.dist-info → epstein_files-1.2.0.dist-info}/METADATA +2 -1
- epstein_files-1.2.0.dist-info/RECORD +34 -0
- epstein_files-1.1.5.dist-info/RECORD +0 -33
- {epstein_files-1.1.5.dist-info → epstein_files-1.2.0.dist-info}/LICENSE +0 -0
- {epstein_files-1.1.5.dist-info → epstein_files-1.2.0.dist-info}/WHEEL +0 -0
- {epstein_files-1.1.5.dist-info → epstein_files-1.2.0.dist-info}/entry_points.txt +0 -0
epstein_files/util/output.py
CHANGED
|
@@ -1,28 +1,31 @@
|
|
|
1
1
|
import json
|
|
2
|
+
from os import unlink
|
|
3
|
+
from typing import cast
|
|
2
4
|
|
|
3
5
|
from rich.padding import Padding
|
|
4
6
|
|
|
5
7
|
from epstein_files.documents.document import Document
|
|
6
|
-
from epstein_files.documents.email import
|
|
8
|
+
from epstein_files.documents.email import Email
|
|
7
9
|
from epstein_files.documents.messenger_log import MessengerLog
|
|
8
10
|
from epstein_files.documents.other_file import FIRST_FEW_LINES, OtherFile
|
|
9
11
|
from epstein_files.epstein_files import EpsteinFiles, count_by_month
|
|
12
|
+
from epstein_files.person import Person
|
|
10
13
|
from epstein_files.util.constant import output_files
|
|
11
14
|
from epstein_files.util.constant.html import *
|
|
12
15
|
from epstein_files.util.constant.names import *
|
|
13
|
-
from epstein_files.util.constant.output_files import JSON_FILES_JSON_PATH, JSON_METADATA_PATH
|
|
14
|
-
from epstein_files.util.constant.strings import
|
|
15
|
-
from epstein_files.util.data import dict_sets_to_lists,
|
|
16
|
+
from epstein_files.util.constant.output_files import EMAILERS_TABLE_PNG_PATH, JSON_FILES_JSON_PATH, JSON_METADATA_PATH
|
|
17
|
+
from epstein_files.util.constant.strings import AUTHOR, TIMESTAMP_STYLE
|
|
18
|
+
from epstein_files.util.data import dict_sets_to_lists, uniquify
|
|
16
19
|
from epstein_files.util.env import args
|
|
17
20
|
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)
|
|
20
21
|
from epstein_files.util.logging import logger
|
|
21
22
|
from epstein_files.util.rich import *
|
|
22
23
|
|
|
24
|
+
DEVICE_SIGNATURE_SUBTITLE = f"Email [italic]Sent from \\[DEVICE][/italic] Signature Breakdown"
|
|
25
|
+
DEVICE_SIGNATURE = 'Device Signature'
|
|
26
|
+
DEVICE_SIGNATURE_PADDING = (1, 0)
|
|
23
27
|
OTHER_INTERESTING_EMAILS_SUBTITLE = 'Other Interesting Emails\n(these emails have been flagged as being of particular interest)'
|
|
24
28
|
PRINT_COLOR_KEY_EVERY_N_EMAILS = 150
|
|
25
|
-
ALT_INFO_STYLE = 'medium_purple4'
|
|
26
29
|
|
|
27
30
|
# Order matters. Default names to print emails for.
|
|
28
31
|
DEFAULT_EMAILERS = [
|
|
@@ -58,20 +61,18 @@ INTERESTING_EMAIL_IDS = [
|
|
|
58
61
|
'030630', # 'What happens with zubair's project?'
|
|
59
62
|
'033178', # 'How is it going with Zubair?'
|
|
60
63
|
'022396', # Ukraine friend
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
'
|
|
66
|
-
'
|
|
67
|
-
INTELLIGENCE_SQUARED,
|
|
68
|
-
UNKNOWN,
|
|
64
|
+
'026505', # I know how dirty trump is
|
|
65
|
+
'029679', # Trump's driver was the bag man
|
|
66
|
+
'030781', '026258', '026260', # Bannon cripto coin issues
|
|
67
|
+
'023627', # Michael Wolff article w/Brock
|
|
68
|
+
'032359', # Jabor e-currency
|
|
69
|
+
#'023208', # Extremely long Leon Black email chain
|
|
69
70
|
]
|
|
70
71
|
|
|
71
72
|
|
|
72
73
|
def print_email_timeline(epstein_files: EpsteinFiles) -> None:
|
|
73
74
|
"""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.
|
|
75
|
+
emails = Document.sort_by_timestamp([e for e in epstein_files.non_duplicate_emails() if not e.is_mailing_list()])
|
|
75
76
|
title = f'Table of All {len(emails):,} Non-Junk Emails in Chronological Order (actual emails below)'
|
|
76
77
|
table = Email.build_emails_table(emails, title=title, show_length=True)
|
|
77
78
|
console.print(Padding(table, (2, 0)))
|
|
@@ -82,30 +83,46 @@ def print_email_timeline(epstein_files: EpsteinFiles) -> None:
|
|
|
82
83
|
console.print(email)
|
|
83
84
|
|
|
84
85
|
|
|
86
|
+
def print_emailers_info_png(epstein_files: EpsteinFiles) -> None:
|
|
87
|
+
print_color_key()
|
|
88
|
+
console.line()
|
|
89
|
+
all_emailers = sorted(epstein_files.emailers(), key=lambda person: person.sort_key())
|
|
90
|
+
console.print(Person.emailer_info_table(all_emailers))
|
|
91
|
+
svg_path = f"{EMAILERS_TABLE_PNG_PATH}.svg"
|
|
92
|
+
console.save_svg(svg_path, theme=HTML_TERMINAL_THEME, title="Epstein Emailers")
|
|
93
|
+
log_file_write(svg_path)
|
|
94
|
+
import cairosvg
|
|
95
|
+
cairosvg.svg2png(url=svg_path, write_to=str(EMAILERS_TABLE_PNG_PATH))
|
|
96
|
+
log_file_write(EMAILERS_TABLE_PNG_PATH)
|
|
97
|
+
unlink(svg_path)
|
|
98
|
+
|
|
99
|
+
|
|
85
100
|
def print_emails_section(epstein_files: EpsteinFiles) -> list[Email]:
|
|
86
101
|
"""Returns emails that were printed (may contain dupes if printed for both author and recipient)."""
|
|
87
102
|
print_section_header(('Selections from ' if not args.all_emails else '') + 'His Emails')
|
|
88
|
-
|
|
89
|
-
already_printed_emails: list[Email] = []
|
|
103
|
+
all_emailers = sorted(epstein_files.emailers(), key=lambda person: person.earliest_email_at())
|
|
90
104
|
num_emails_printed_since_last_color_key = 0
|
|
105
|
+
printed_emails: list[Email] = []
|
|
106
|
+
people_to_print: list[Person]
|
|
91
107
|
|
|
92
108
|
if args.names:
|
|
93
|
-
|
|
109
|
+
people_to_print = epstein_files.person_objs(args.names)
|
|
94
110
|
else:
|
|
95
111
|
if args.all_emails:
|
|
96
|
-
|
|
112
|
+
people_to_print = all_emailers
|
|
97
113
|
else:
|
|
98
|
-
|
|
114
|
+
people_to_print = epstein_files.person_objs(DEFAULT_EMAILERS)
|
|
99
115
|
|
|
100
116
|
print_other_page_link(epstein_files)
|
|
101
|
-
|
|
102
|
-
console.print(_table_of_selected_emailers(emailers_to_print, epstein_files))
|
|
103
|
-
console.print(Padding(_all_emailers_table(epstein_files), (2, 0)))
|
|
117
|
+
print_centered(Padding(Person.emailer_info_table(all_emailers, people_to_print), (2, 0, 1, 0)))
|
|
104
118
|
|
|
105
|
-
for
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
119
|
+
for person in people_to_print:
|
|
120
|
+
if person.name in epstein_files.uninteresting_emailers() and not args.names:
|
|
121
|
+
continue
|
|
122
|
+
|
|
123
|
+
printed_person_emails = person.print_emails()
|
|
124
|
+
printed_emails.extend(printed_person_emails)
|
|
125
|
+
num_emails_printed_since_last_color_key += len(printed_person_emails)
|
|
109
126
|
|
|
110
127
|
# Print color key every once in a while
|
|
111
128
|
if num_emails_printed_since_last_color_key > PRINT_COLOR_KEY_EVERY_N_EMAILS:
|
|
@@ -113,26 +130,28 @@ def print_emails_section(epstein_files: EpsteinFiles) -> list[Email]:
|
|
|
113
130
|
num_emails_printed_since_last_color_key = 0
|
|
114
131
|
|
|
115
132
|
if args.names:
|
|
116
|
-
return
|
|
133
|
+
return printed_emails
|
|
117
134
|
|
|
118
135
|
# Print other interesting emails
|
|
119
|
-
|
|
120
|
-
extra_emails = [e for e in epstein_files.for_ids(INTERESTING_EMAIL_IDS) if e.file_id not in
|
|
121
|
-
print_subtitle_panel(OTHER_INTERESTING_EMAILS_SUBTITLE)
|
|
122
|
-
console.line()
|
|
136
|
+
printed_email_ids = [email.file_id for email in printed_emails]
|
|
137
|
+
extra_emails = [e for e in epstein_files.for_ids(INTERESTING_EMAIL_IDS) if e.file_id not in printed_email_ids]
|
|
123
138
|
|
|
124
|
-
|
|
125
|
-
|
|
139
|
+
if len(extra_emails) > 0:
|
|
140
|
+
print_subtitle_panel(OTHER_INTERESTING_EMAILS_SUBTITLE)
|
|
141
|
+
console.line()
|
|
126
142
|
|
|
127
|
-
|
|
143
|
+
for other_email in extra_emails:
|
|
144
|
+
console.print(other_email)
|
|
145
|
+
printed_emails.append(cast(Email, other_email))
|
|
128
146
|
|
|
129
147
|
if args.all_emails:
|
|
130
|
-
_verify_all_emails_were_printed(epstein_files,
|
|
148
|
+
_verify_all_emails_were_printed(epstein_files, printed_emails)
|
|
131
149
|
|
|
132
|
-
|
|
133
|
-
|
|
150
|
+
_print_email_device_info(epstein_files)
|
|
151
|
+
fwded_articles = [e for e in printed_emails if e.config and e.is_fwded_article()]
|
|
152
|
+
log_msg = f"Rewrote {len(Email.rewritten_header_ids)} of {len(printed_emails)} email headers"
|
|
134
153
|
logger.warning(f"{log_msg}, {len(fwded_articles)} of the emails were forwarded articles.")
|
|
135
|
-
return
|
|
154
|
+
return printed_emails
|
|
136
155
|
|
|
137
156
|
|
|
138
157
|
def print_json_files(epstein_files: EpsteinFiles):
|
|
@@ -165,12 +184,12 @@ def print_json_stats(epstein_files: EpsteinFiles) -> None:
|
|
|
165
184
|
console.line(5)
|
|
166
185
|
console.print(Panel('JSON Stats Dump', expand=True, style='reverse bold'), '\n')
|
|
167
186
|
print_json(f"MessengerLog Sender Counts", MessengerLog.count_authors(epstein_files.imessage_logs), skip_falsey=True)
|
|
168
|
-
print_json(f"Email Author Counts", epstein_files.email_author_counts, skip_falsey=True)
|
|
169
|
-
print_json(f"Email Recipient Counts", epstein_files.email_recipient_counts, skip_falsey=True)
|
|
187
|
+
print_json(f"Email Author Counts", epstein_files.email_author_counts(), skip_falsey=True)
|
|
188
|
+
print_json(f"Email Recipient Counts", epstein_files.email_recipient_counts(), skip_falsey=True)
|
|
170
189
|
print_json("Email signature_substitution_countss", epstein_files.email_signature_substitution_counts(), skip_falsey=True)
|
|
171
|
-
print_json("email_author_device_signatures", dict_sets_to_lists(epstein_files.email_authors_to_device_signatures))
|
|
172
|
-
print_json("email_sent_from_devices", dict_sets_to_lists(epstein_files.email_device_signatures_to_authors))
|
|
173
|
-
print_json("
|
|
190
|
+
print_json("email_author_device_signatures", dict_sets_to_lists(epstein_files.email_authors_to_device_signatures()))
|
|
191
|
+
print_json("email_sent_from_devices", dict_sets_to_lists(epstein_files.email_device_signatures_to_authors()))
|
|
192
|
+
print_json("unknown_recipient_ids", epstein_files.unknown_recipient_ids())
|
|
174
193
|
print_json("count_by_month", count_by_month(epstein_files.all_documents()))
|
|
175
194
|
|
|
176
195
|
|
|
@@ -226,113 +245,26 @@ def write_urls() -> None:
|
|
|
226
245
|
logger.warning(f"Wrote {len(url_vars)} URL variables to '{URLS_ENV}'\n")
|
|
227
246
|
|
|
228
247
|
|
|
229
|
-
def
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
emailer_counts = {
|
|
250
|
-
emailer: epstein_files.email_author_counts[emailer] + epstein_files.email_recipient_counts[emailer]
|
|
251
|
-
for emailer in epstein_files.all_emailers(include_useless=True)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
for name, count in sort_dict(emailer_counts):
|
|
255
|
-
style = get_style_for_name(name, default_style=DEFAULT_NAME_STYLE)
|
|
256
|
-
emails = epstein_files.emails_for(name)
|
|
257
|
-
|
|
258
|
-
counts_table.add_row(
|
|
259
|
-
Text.from_markup(link_markup(epsteinify_name_url(name or UNKNOWN), name or UNKNOWN, style)),
|
|
260
|
-
f"{count:,}",
|
|
261
|
-
str(epstein_files.email_author_counts[name]),
|
|
262
|
-
str(epstein_files.email_recipient_counts[name]),
|
|
263
|
-
emails[0].date_str(),
|
|
264
|
-
emails[-1].date_str(),
|
|
265
|
-
f"{epstein_files.email_conversation_length_in_days(name)}",
|
|
266
|
-
link_text_obj(search_jmail_url(name), JMAIL) if name else '',
|
|
267
|
-
link_text_obj(epstein_media_person_url(name), EPSTEIN_MEDIA) if _is_ok_for_epstein_web(name) else '',
|
|
268
|
-
link_text_obj(epstein_web_person_url(name), EPSTEIN_WEB) if _is_ok_for_epstein_web(name) else '',
|
|
269
|
-
link_text_obj(search_twitter_url(name), 'search X') if name else '',
|
|
270
|
-
)
|
|
271
|
-
|
|
272
|
-
return counts_table
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
def _is_ok_for_epstein_web(name: str | None) -> bool:
|
|
276
|
-
"""Return True if it's likely that EpsteinWeb has a page for this name."""
|
|
277
|
-
if name is None or ' ' not in name:
|
|
278
|
-
return False
|
|
279
|
-
elif '@' in name or '/' in name or '??' in name:
|
|
280
|
-
return False
|
|
281
|
-
elif name in INVALID_FOR_EPSTEIN_WEB:
|
|
282
|
-
return False
|
|
283
|
-
|
|
284
|
-
return True
|
|
285
|
-
|
|
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
|
|
248
|
+
def _print_email_device_info(epstein_files: EpsteinFiles) -> None:
|
|
249
|
+
print_subtitle_panel(DEVICE_SIGNATURE_SUBTITLE)
|
|
250
|
+
console.print(_signature_table(epstein_files.email_device_signatures_to_authors(), (DEVICE_SIGNATURE, AUTHOR), ', '))
|
|
251
|
+
console.print(_signature_table(epstein_files.email_authors_to_device_signatures(), (AUTHOR, DEVICE_SIGNATURE)))
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _signature_table(keyed_sets: dict[str, set[str]], cols: tuple[str, str], join_char: str = '\n') -> Padding:
|
|
255
|
+
"""Build table for who signed emails with 'Sent from my iPhone' etc."""
|
|
256
|
+
title = 'Email Signatures Used By Authors' if cols[0] == AUTHOR else 'Authors Seen Using Email Signatures'
|
|
257
|
+
table = build_table(title, header_style="bold reverse", show_lines=True)
|
|
258
|
+
|
|
259
|
+
for i, col in enumerate(cols):
|
|
260
|
+
table.add_column(col.title() + ('s' if i == 1 else ''))
|
|
261
|
+
|
|
262
|
+
new_dict = dict_sets_to_lists(keyed_sets)
|
|
263
|
+
|
|
264
|
+
for k in sorted(new_dict.keys()):
|
|
265
|
+
table.add_row(highlighter(k or UNKNOWN), highlighter(join_char.join(sorted(new_dict[k]))))
|
|
266
|
+
|
|
267
|
+
return Padding(table, DEVICE_SIGNATURE_PADDING)
|
|
336
268
|
|
|
337
269
|
|
|
338
270
|
def _verify_all_emails_were_printed(epstein_files: EpsteinFiles, already_printed_emails: list[Email]) -> None:
|
epstein_files/util/rich.py
CHANGED
|
@@ -18,7 +18,7 @@ from epstein_files.util.constant.names import UNKNOWN
|
|
|
18
18
|
from epstein_files.util.constant.strings import DEFAULT, EMAIL, NA, TEXT_MESSAGE
|
|
19
19
|
from epstein_files.util.constant.urls import *
|
|
20
20
|
from epstein_files.util.constants import HEADER_ABBREVIATIONS
|
|
21
|
-
from epstein_files.util.data import json_safe
|
|
21
|
+
from epstein_files.util.data import json_safe
|
|
22
22
|
from epstein_files.util.env import args
|
|
23
23
|
from epstein_files.util.file_helper import log_file_write
|
|
24
24
|
from epstein_files.util.highlighted_group import ALL_HIGHLIGHTS, HIGHLIGHTED_NAMES, EpsteinHighlighter
|
|
@@ -26,11 +26,11 @@ from epstein_files.util.logging import logger
|
|
|
26
26
|
|
|
27
27
|
TITLE_WIDTH = 50
|
|
28
28
|
SUBTITLE_WIDTH = 110
|
|
29
|
-
MIN_AUTHOR_PANEL_WIDTH = 80
|
|
30
29
|
NUM_COLOR_KEY_COLS = 4
|
|
31
30
|
NA_TXT = Text(NA, style='dim')
|
|
32
31
|
SUBTITLE_PADDING = (2, 0, 1, 0)
|
|
33
32
|
GREY_NUMBERS = [58, 39, 39, 35, 30, 27, 23, 23, 19, 19, 15, 15, 15]
|
|
33
|
+
VALID_GREYS = [0, 3, 7, 11, 15, 19, 23, 27, 30, 35, 37, 39, 42, 46, 50, 53, 54, 58, 62, 63, 66, 69, 70, 74, 78, 82, 84, 85, 89, 93]
|
|
34
34
|
|
|
35
35
|
INFO_STYLE = 'white dim italic'
|
|
36
36
|
KEY_STYLE = 'honeydew2 bold'
|
|
@@ -108,7 +108,7 @@ def build_highlighter(pattern: str) -> EpsteinHighlighter:
|
|
|
108
108
|
return TempHighlighter()
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
def build_table(title: str | None, cols: list[str | dict] | None = None, **kwargs) -> Table:
|
|
111
|
+
def build_table(title: str | Text | None, cols: list[str | dict] | None = None, **kwargs) -> Table:
|
|
112
112
|
table = Table(title=title, **{**DEFAULT_TABLE_KWARGS, **kwargs})
|
|
113
113
|
|
|
114
114
|
if cols:
|
|
@@ -149,19 +149,6 @@ def parenthesize(msg: str | Text, style: str = '') -> Text:
|
|
|
149
149
|
return Text('(', style=style).append(txt).append(')')
|
|
150
150
|
|
|
151
151
|
|
|
152
|
-
def print_author_panel(msg: str, footer: str | None, style: str | None) -> None:
|
|
153
|
-
"""Print a panel with the name of an emailer and a few tidbits of information about them."""
|
|
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)]
|
|
158
|
-
|
|
159
|
-
if footer:
|
|
160
|
-
elements.append(Text(f"({footer})", justify='center', style=f"{style} italic"))
|
|
161
|
-
|
|
162
|
-
print_centered(Padding(Group(*elements), (2, 0, 1, 0)))
|
|
163
|
-
|
|
164
|
-
|
|
165
152
|
def print_centered(obj: RenderableType, style: str = '') -> None:
|
|
166
153
|
console.print(Align.center(obj), style=style)
|
|
167
154
|
|
|
@@ -234,13 +221,11 @@ def print_json(label: str, obj: object, skip_falsey: bool = False) -> None:
|
|
|
234
221
|
|
|
235
222
|
|
|
236
223
|
def print_other_page_link(epstein_files: 'EpsteinFiles') -> None:
|
|
237
|
-
markup_msg = link_markup(other_site_url(), 'the other page', style='light_slate_grey bold')
|
|
238
|
-
|
|
239
224
|
if other_site_type() == EMAIL:
|
|
240
|
-
txt = Text
|
|
225
|
+
txt = THE_OTHER_PAGE_TXT + Text(f' is uncurated and has all {len(epstein_files.emails):,} emails')
|
|
241
226
|
txt.append(f" and {len(epstein_files.other_files)} unclassifiable files")
|
|
242
227
|
else:
|
|
243
|
-
txt =
|
|
228
|
+
txt = THE_OTHER_PAGE_TXT + (f' displays a limited collection of emails and')
|
|
244
229
|
txt.append(" unclassifiable files of particular interest")
|
|
245
230
|
|
|
246
231
|
print_centered(parenthesize(txt), style=OTHER_PAGE_MSG_STYLE)
|
|
@@ -311,7 +296,7 @@ def write_html(output_path: Path | None) -> None:
|
|
|
311
296
|
def _print_abbreviations_table() -> None:
|
|
312
297
|
table = build_table(title="Abbreviations Used Frequently In These Conversations", show_header=False)
|
|
313
298
|
table.add_column("Abbreviation", justify="center", style='bold')
|
|
314
|
-
table.add_column("Translation",
|
|
299
|
+
table.add_column("Translation", justify="center", min_width=62, style="white")
|
|
315
300
|
|
|
316
301
|
for k, v in HEADER_ABBREVIATIONS.items():
|
|
317
302
|
table.add_row(highlighter(k), v)
|
epstein_files/util/word_count.py
CHANGED
|
@@ -197,7 +197,7 @@ def write_word_counts_html() -> None:
|
|
|
197
197
|
email_subjects: set[str] = set()
|
|
198
198
|
word_count = WordCount()
|
|
199
199
|
# Remove dupes, junk mail, and fwded articles from emails
|
|
200
|
-
emails = [e for e in epstein_files.non_duplicate_emails() if not (e.
|
|
200
|
+
emails = [e for e in epstein_files.non_duplicate_emails() if not (e.is_mailing_list() or e.is_fwded_article())]
|
|
201
201
|
|
|
202
202
|
for email in emails:
|
|
203
203
|
if args.names and email.author not in args.names:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: epstein-files
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.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
|
|
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Requires-Dist: cairosvg (>=2.8.2,<3.0.0)
|
|
20
21
|
Requires-Dist: datefinder (>=0.7.3,<0.8.0)
|
|
21
22
|
Requires-Dist: inflection (>=0.5.1,<0.6.0)
|
|
22
23
|
Requires-Dist: python-dateutil (>=2.9.0.post0,<3.0.0)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
epstein_files/__init__.py,sha256=HfNEIZ8I8h44GdWTaS0UTA2NjIGZYoqcdTFGV9OnACM,5403
|
|
2
|
+
epstein_files/documents/communication.py,sha256=NzcZ3vQBjVAovasnxpUyII4weycMaJ2T3fc_8d4eg-U,1875
|
|
3
|
+
epstein_files/documents/document.py,sha256=eDPN06KztjWcb6LCmNxqud0fcckfr6ZySHO1TxbjaCY,17643
|
|
4
|
+
epstein_files/documents/email.py,sha256=7zLkvyHdv2fSTKGrT6eW2b6VqaTXpBWPzCKXqOQwl6s,42083
|
|
5
|
+
epstein_files/documents/emails/email_header.py,sha256=P2dZno30W49oqf9MixiAZbtxX1ooe-yJ6VVa_R24ySw,7588
|
|
6
|
+
epstein_files/documents/imessage/text_message.py,sha256=Tx81FVz8LwQwC_ZhMa5XI-qlF2jPNxbAnC2KN1d0fdk,3353
|
|
7
|
+
epstein_files/documents/json_file.py,sha256=WcZW5NNqA67rHTdopbOGtup00muNaLlvrNgKb-K4zO8,1504
|
|
8
|
+
epstein_files/documents/messenger_log.py,sha256=1bv62WoQMKR3gYDrK9W3Xm7cqLbKrkRBV7NTFL2cexE,7349
|
|
9
|
+
epstein_files/documents/other_file.py,sha256=TeEzsfGN_mTFZPhfyt9ihxK9oTCYwI8sRLplTsgpOMY,9893
|
|
10
|
+
epstein_files/epstein_files.py,sha256=7xEgs9rKQen6mNPCk2drWc7VsXg9OylDnmzFk8U4UEM,14500
|
|
11
|
+
epstein_files/person.py,sha256=ZzU2WNEdUXJpwA8M3y99AhfK1oQVlnOy-SNP2b8JkjI,14700
|
|
12
|
+
epstein_files/util/constant/common_words.py,sha256=C1JERPnOGHV2UMC71aEue1i9QTQog-RfT3IzdcYQOYQ,3702
|
|
13
|
+
epstein_files/util/constant/html.py,sha256=MFooFV8KfFBCm9hL1u6A3hi_u37i7lL6UKAYoKQj3PI,1505
|
|
14
|
+
epstein_files/util/constant/names.py,sha256=iaF4vJnbqatC1evCBwFagqCQtQr15OUJC_jYbHgoEfs,11058
|
|
15
|
+
epstein_files/util/constant/output_files.py,sha256=gUZJ4mNoeJy3qTYWr_jhSmQI-_uV_jdLR0YCiaQd_Qg,1982
|
|
16
|
+
epstein_files/util/constant/strings.py,sha256=YgrUSf3fANRw2jr1r8HrmbZpzET7KY7gn-MrrZ4-bQI,2017
|
|
17
|
+
epstein_files/util/constant/urls.py,sha256=6cJVEGFWwQ5CCq5oy-Ukj2-xZvbclMWySVxykaFW-W4,5599
|
|
18
|
+
epstein_files/util/constants.py,sha256=HJK7c9T2y6CxQS1CtB8qObCL6hQ6M92xlH5deJZbc8M,121602
|
|
19
|
+
epstein_files/util/data.py,sha256=CXvQBWZuAnkxOuOv2KtwIZe7jtHtfqAOd6ELIhNeJzI,2983
|
|
20
|
+
epstein_files/util/doc_cfg.py,sha256=RjKRX6BGTJf0J4vAWdEH6kMtfG75Psu3ntNx4MYnMzY,9509
|
|
21
|
+
epstein_files/util/env.py,sha256=6DvDCDdG_2AwXR2sOO5kL3GHtZFQxHs8HT87VjVt9NE,6718
|
|
22
|
+
epstein_files/util/file_helper.py,sha256=MpG1hI7DGs05fV9KSVb_ltc98DC8tv1E_TTo5X_E7Js,3010
|
|
23
|
+
epstein_files/util/highlighted_group.py,sha256=c7p7_P-DfbSqrxIh8Es4A3aFxQFrsv9WBodzqMrVcAQ,55708
|
|
24
|
+
epstein_files/util/logging.py,sha256=F45YqEKAiIb0rDZnOB7XuaY-dOkOKrsfSzO1VVqY508,2097
|
|
25
|
+
epstein_files/util/output.py,sha256=8Hqt-CV_02-GyETrcqm4AM6dWO0OTV9nddOQNJ7eW8k,11836
|
|
26
|
+
epstein_files/util/rich.py,sha256=J4b6TLaJ3HQFQdo5Iib60lRwrjzlHRW2zWEaobkrSEc,13698
|
|
27
|
+
epstein_files/util/search_result.py,sha256=1fxe0KPBQXBk4dLfu6m0QXIzYfZCzvaSkWqvghJGzxY,567
|
|
28
|
+
epstein_files/util/timer.py,sha256=QqqXAQofKPWkDngNwG0mOqRn7nHcAR-BGQjqAwZfXoE,840
|
|
29
|
+
epstein_files/util/word_count.py,sha256=rs-bsSMnGG1BnYUmxYuBLZ8X0-3-jJdaB03v1jOIq4g,9202
|
|
30
|
+
epstein_files-1.2.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
31
|
+
epstein_files-1.2.0.dist-info/METADATA,sha256=ttXba8M6jt-gbb94ouqD95zOMdTosW8HSUfFJiDLFlI,6032
|
|
32
|
+
epstein_files-1.2.0.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
|
|
33
|
+
epstein_files-1.2.0.dist-info/entry_points.txt,sha256=5qYgwAXpxegeAicD_rzda_trDRnUC51F5UVDpcZ7j6Q,240
|
|
34
|
+
epstein_files-1.2.0.dist-info/RECORD,,
|
|
@@ -1,33 +0,0 @@
|
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|