epstein-files 1.0.15__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 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, print_header, print_panel, write_html
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
- print_header(epstein_files)
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
- write_html(ALL_EMAILS_PATH if args.all_emails else TEXT_MSGS_HTML_PATH)
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())
@@ -135,6 +135,7 @@ JUNK_EMAILERS = [
135
135
  ]
136
136
 
137
137
  MAILING_LISTS = [
138
+ CAROLYN_RANGEL,
138
139
  INTELLIGENCE_SQUARED,
139
140
  'middle.east.update@hotmail.com',
140
141
  JP_MORGAN_USGIO,
@@ -342,6 +343,9 @@ class Email(Communication):
342
343
  else:
343
344
  for recipient in self.header.recipients():
344
345
  self.recipients.extend(self._emailer_names(recipient))
346
+
347
+ if self.author in MAILING_LISTS and (len(self.recipients) == 0 or self.recipients == [self.author]):
348
+ self.recipients = [JEFFREY_EPSTEIN] # Assume mailing list emails are to Epstein
345
349
  except Exception as e:
346
350
  console.print_exception()
347
351
  console.line(2)
@@ -362,7 +366,7 @@ class Email(Communication):
362
366
  def info_txt(self) -> Text:
363
367
  email_type = 'fwded article' if self.is_fwded_article() else 'email'
364
368
  txt = Text(f"OCR text of {email_type} from ", style='grey46').append(self.author_txt).append(' to ')
365
- return txt.append(self._recipients_txt()).append(highlighter(f" probably sent at {self.timestamp}"))
369
+ return txt.append(self.recipients_txt()).append(highlighter(f" probably sent at {self.timestamp}"))
366
370
 
367
371
  def is_fwded_article(self) -> bool:
368
372
  return bool(self.config and self.config.is_fwded_article)
@@ -378,6 +382,16 @@ class Email(Communication):
378
382
  metadata.update({k: v for k, v in local_metadata.items() if v and k in METADATA_FIELDS})
379
383
  return metadata
380
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
+
381
395
  def subject(self) -> str:
382
396
  return self.header.subject or ''
383
397
 
@@ -386,7 +400,7 @@ class Email(Communication):
386
400
  txt = self._summary()
387
401
 
388
402
  if len(self.recipients) > 0:
389
- txt.append(', ').append(key_value_txt('recipients', self._recipients_txt()))
403
+ txt.append(', ').append(key_value_txt('recipients', self.recipients_txt()))
390
404
 
391
405
  return txt.append(CLOSE_PROPERTIES_CHAR)
392
406
 
@@ -549,16 +563,6 @@ class Email(Communication):
549
563
 
550
564
  return collapse_newlines(text).strip()
551
565
 
552
- def _recipients_txt(self) -> Text:
553
- """Text object with comma separated colored versions of all recipients."""
554
- recipients = [r or UNKNOWN for r in self.recipients] if len(self.recipients) > 0 else [UNKNOWN]
555
-
556
- # Use just the last name for each recipient if there's 3 or more recipients
557
- return join_texts([
558
- Text(r if len(recipients) < 3 else extract_last_name(r), style=get_style_for_name(r))
559
- for r in recipients
560
- ], join=', ')
561
-
562
566
  def _remove_line(self, idx: int) -> None:
563
567
  """Remove a line from self.lines."""
564
568
  num_lines = idx * 2
@@ -590,7 +594,7 @@ class Email(Communication):
590
594
  self._merge_lines(3) # Merge 4th and 5th rows
591
595
  elif self.file_id in '026609 029402 032405 022695'.split():
592
596
  self._merge_lines(4) # Merge 5th and 6th rows
593
- elif self.file_id in ['019407', '031980', '030384', '033144', '030999', '033575', '029835', '030381', '033357']:
597
+ elif self.file_id in ['019407', '031980', '030384', '033144', '030999', '033575', '029835', '030381', '033357', '026924']:
594
598
  self._merge_lines(2, 4)
595
599
  elif self.file_id in ['029154', '029163']:
596
600
  self._merge_lines(2, 5)
@@ -693,7 +697,7 @@ class Email(Communication):
693
697
  self.config.description = extracted_description
694
698
 
695
699
  self.config.is_interesting = self.config.is_interesting or extracted_from_doc_cfg.is_interesting
696
- self.warn(f"Constructed synthetic config: {self.config}")
700
+ self.log(f"Constructed synthetic config: {self.config}")
697
701
 
698
702
  def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
699
703
  logger.debug(f"Printing '{self.filename}'...")
@@ -75,7 +75,7 @@ class TextMessage:
75
75
  return msg_txt
76
76
 
77
77
  def __rich__(self) -> Text:
78
+ timestamp_txt = Text(f"[{self.timestamp_str}]", style=TIMESTAMP_DIM).append(' ')
78
79
  author_style = get_style_for_name(self.author_str if self.author_str.startswith('+') else self.author)
79
80
  author_txt = Text(self.author_str, style=author_style)
80
- timestamp_txt = Text(f"[{self.timestamp_str}]", style=TIMESTAMP_DIM).append(' ')
81
81
  return Text('').append(timestamp_txt).append(author_txt).append(': ', style='dim').append(self._message())
@@ -12,7 +12,7 @@ from epstein_files.documents.communication import Communication
12
12
  from epstein_files.documents.imessage.text_message import TextMessage
13
13
  from epstein_files.util.constant.names import JEFFREY_EPSTEIN, UNKNOWN
14
14
  from epstein_files.util.constant.strings import AUTHOR, TIMESTAMP_STYLE
15
- from epstein_files.util.data import days_between, days_between_str, iso_timestamp, listify, sort_dict
15
+ from epstein_files.util.data import days_between, days_between_str, iso_timestamp, sort_dict
16
16
  from epstein_files.util.doc_cfg import Metadata, TextCfg
17
17
  from epstein_files.util.highlighted_group import get_style_for_name
18
18
  from epstein_files.util.logging import logger
@@ -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_all_files_page_link, build_table, console, highlighter, link_text_obj, link_markup, print_author_header, print_centered,
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
- print_author_header(
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 {'Emails' if args.all_emails else 'Text Messages'}</title>" + """
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;">
@@ -38,6 +38,7 @@ BRAD_EDWARDS = 'Brad Edwards'
38
38
  BRAD_KARP = 'Brad Karp'
39
39
  BRAD_WECHSLER = 'Brad Wechsler'
40
40
  BORIS_NIKOLIC = 'Boris Nikolic'
41
+ CAROLYN_RANGEL = 'Carolyn Rangel'
41
42
  CECILE_DE_JONGH = 'Cecile de Jongh'
42
43
  CECILIA_STEEN = 'Cecilia Steen'
43
44
  CELINA_DUBIN = 'Celina Dubin'
@@ -238,7 +239,7 @@ OTHER_NAMES = NAMES_TO_NOT_HIGHLIGHT + """
238
239
  gardner gary geoff geoffrey gilbert gloria goldberg gonzalez gould graham greene guarino gwyneth
239
240
  hancock harold harrison harry hay helen hill hirsch hofstadter horowitz hussein
240
241
  ian isaac isaacson
241
- 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
242
243
  karl kate kathy kelly kim kruger kyle
243
244
  laurie lawrence leo leonard lenny leslie lieberman louis lynch lynn
244
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,
@@ -6,6 +6,7 @@ from inflection import parameterize
6
6
  from rich.text import Text
7
7
 
8
8
  from epstein_files.util.constant.output_files import *
9
+ from epstein_files.util.env import args
9
10
  from epstein_files.util.file_helper import coerce_file_stem
10
11
 
11
12
  # Style stuff
@@ -101,4 +102,12 @@ def link_text_obj(url: str, link_text: str | None = None, style: str = ARCHIVE_L
101
102
  return Text.from_markup(link_markup(url, link_text, style))
102
103
 
103
104
 
105
+ def other_site_type() -> SiteType:
106
+ return TEXT_MESSAGE if args.all_emails else EMAIL
107
+
108
+
109
+ def other_site_url() -> str:
110
+ return SITE_URLS[other_site_type()]
111
+
112
+
104
113
  CRYPTADAMUS_TWITTER = link_markup('https://x.com/cryptadamist', '@cryptadamist')
@@ -198,7 +198,7 @@ EMAILERS = [
198
198
  'Vladimir Yudashkin',
199
199
  ]
200
200
 
201
- EMAILER_REGEXES = deepcopy(EMAILER_ID_REGEXES)
201
+ EMAILER_REGEXES = deepcopy(EMAILER_ID_REGEXES) # Keep a copy without the simple EMAILERS regexes
202
202
 
203
203
  # Add simple matching regexes for EMAILERS entries to EMAILER_REGEXES
204
204
  for emailer in EMAILERS:
@@ -536,6 +536,7 @@ EMAILS_CONFIG = [
536
536
  EmailCfg(id='033274', recipients=[JEFFREY_EPSTEIN]), # this is a note sent to self
537
537
  EmailCfg(id='032780', recipients=[JEFFREY_EPSTEIN]), # Bad OCR (nofix)
538
538
  EmailCfg(id='029324', recipients=[JEFFREY_EPSTEIN, "Jojo Fontanilla", "Lyn Fontanilla"]), # Bad OCR (nofix)
539
+ EmailCfg(id='013482', recipients=[JEFFREY_EPSTEIN], is_fwded_article=True), # other recipients redacted. "The view from the US: Stem cell therapy steps up a gear with firs"
539
540
  EmailCfg(id='033456', recipients=["Joel"], attribution_reason='Reply'),
540
541
  EmailCfg(id='033458', recipients=["Joel"], attribution_reason='Reply'),
541
542
  EmailCfg(id='033460', recipients=["Joel"], attribution_reason='Reply'),
@@ -626,7 +627,6 @@ EMAILS_CONFIG = [
626
627
  EmailCfg(id='021740', is_fwded_article=True), # Miami Herald article about Epstein prosecutor
627
628
  EmailCfg(id='023126', is_fwded_article=True), # Miami Herald on Alex Acosta
628
629
  EmailCfg(id='029625', is_fwded_article=True), # Conchita Sarnoff Daily Beast Articles - Epstein Sex Trafficking Investigation and Settlement
629
- EmailCfg(id='013482', is_fwded_article=True), # The view from the US: Stem cell therapy steps up a gear with firs
630
630
  EmailCfg(id='029505', is_fwded_article=True), # Foreign Policy Middle Eastern Monarchs Look at the Trump
631
631
  EmailCfg(id='029859', is_fwded_article=True), # Palm Beach Post: Epstein paid three women $5.5 million to end lawsuits
632
632
  EmailCfg(id='031988', is_fwded_article=True), # NYT review of Inside Job
@@ -1005,7 +1005,7 @@ OTHER_FILES_ARTICLES = [
1005
1005
  DocCfg(id='023046', author=VI_DAILY_NEWS, description='article', date='2019-02-27'),
1006
1006
  DocCfg(id='031170', author=VI_DAILY_NEWS, description='article', date='2019-03-06'),
1007
1007
  DocCfg(id='016506', author=VI_DAILY_NEWS, description='article', date='2019-02-28'),
1008
- DocCfg(id='016507', author=VI_DAILY_NEWS, description=f"'Perversion of Justice' by {JULIE_K_BROWN}", date='2018-12-19'),
1008
+ DocCfg(id='016507', author=VI_DAILY_NEWS, description=f'"Perversion of Justice" by {JULIE_K_BROWN}', date='2018-12-19'),
1009
1009
  DocCfg(id='019212', author=WAPO, description=f'and Times Tribune articles about Bannon, Trump, and healthcare execs'),
1010
1010
  DocCfg(id='033379', author=WAPO, description=f'"How Washington Pivoted From Finger-Wagging to Appeasement" (about Viktor Orban)', date='2018-05-25'),
1011
1011
  DocCfg(
@@ -1020,12 +1020,12 @@ OTHER_FILES_ARTICLES = [
1020
1020
  DocCfg(id='026648', description=f'article about {JASTA} lawsuit against Saudi Arabia by 9/11 victims (Russian propaganda?)', date='2017-05-13'),
1021
1021
  DocCfg(id='032159', description=f"article about microfinance and cell phones in Zimbabwe, Strive Masiyiwa (Econet Wireless)"),
1022
1022
  DocCfg(id='030825', description=f'{ARTICLE_DRAFT} Syria'),
1023
- DocCfg(id='027051', description=f"German language article about the 2013 Lifeball / AIDS Gala", date='2013-01-01'),
1023
+ DocCfg(id='027051', description=f"German article about the 2013 Lifeball / AIDS Gala", date='2013-01-01', attached_to_email_id='027049'),
1024
1024
  DocCfg(id='033480', description=f"John Bolton press clipping", date='2018-04-06', duplicate_ids=['033481']),
1025
1025
  DocCfg(id='013403', description=f"{LEXIS_NEXIS} result from The Evening Standard about Bernie Madoff", date='2009-12-24'),
1026
1026
  DocCfg(id='021093', description=f"page of unknown article about Epstein and Maxwell"),
1027
1027
  DocCfg(id='031191', description=f"single page of unknown article about Epstein and Trump's relationship in 1997"),
1028
- DocCfg(id='026520', description=f'Spanish language article about {SULTAN_BIN_SULAYEM}', date='2013-09-27'),
1028
+ DocCfg(id='026520', description=f'Spanish article about {SULTAN_BIN_SULAYEM}', date='2013-09-27'),
1029
1029
  DocCfg(
1030
1030
  id='031736',
1031
1031
  description=f"{TRANSLATION} Arabic article by Abdulnaser Salamah 'Trump; Prince of Believers (Caliph)!'",
@@ -1418,7 +1418,7 @@ OTHER_FILES_POLITICS = [
1418
1418
  DocCfg(
1419
1419
  id='025849',
1420
1420
  author='US Office of Government Information Services',
1421
- description=f"Building a Bridge Between FOIA Requesters & Agencies",
1421
+ description=f'"Building a Bridge Between FOIA Requesters & Agencies"',
1422
1422
  ),
1423
1423
  DocCfg(id='031670', description=f"letter from General Mike Flynn's lawyers to senators Mark Warner & Richard Burr about subpoena"),
1424
1424
  DocCfg(
@@ -1496,6 +1496,13 @@ OTHER_FILES_RESUMES = [
1496
1496
  OTHER_FILES_ARTS = [
1497
1497
  DocCfg(id='018703', author=ANDRES_SERRANO, description=f"artist statement about Trump objects"),
1498
1498
  DocCfg(id='023438', author=BROCKMAN_INC, description=f"announcement of auction of 'Noise' by Daniel Kahneman, Olivier Sibony, and Cass Sunstein"),
1499
+ DocCfg(
1500
+ id='025147',
1501
+ author=BROCKMAN_INC,
1502
+ description=f'hot list Frankfurt Book Fair (includes article about Silk Road/Ross Ulbricht)',
1503
+ date='2016-10-23',
1504
+ is_interesting=True,
1505
+ ),
1499
1506
  DocCfg(id='030769', author='Independent Filmmaker Project (IFP)', description=f"2017 Gotham Awards invitation"),
1500
1507
  DocCfg(
1501
1508
  id='025205',
@@ -1520,12 +1527,6 @@ OTHER_FILES_MISC = [
1520
1527
  author=f'linkspirit (French?) and {LAWRENCE_KRAUSS}',
1521
1528
  is_interesting=True, # we don't know who linkspirit is yet
1522
1529
  ),
1523
- DocCfg(
1524
- id='025147',
1525
- author=BROCKMAN_INC,
1526
- description=f'hot list Frankfurt Book Fair (includes article about Silk Road/Ross Ulbricht)',
1527
- date='2016-10-23',
1528
- ),
1529
1530
  DocCfg(id='022494', author='DOJ', description=f'Foreign Corrupt Practices Act (FCPA) Resource Guide'),
1530
1531
  DocCfg(id='023096', author=EPSTEIN_FOUNDATION, description=f'blog post', date='2012-11-15'),
1531
1532
  DocCfg(id='029326', author=EPSTEIN_FOUNDATION, description=f'{PRESS_RELEASE}', date='2013-02-15'),
@@ -1533,7 +1534,7 @@ OTHER_FILES_MISC = [
1533
1534
  DocCfg(id='027071', author=FEMALE_HEALTH_COMPANY, description=f"brochure requesting donations for female condoms in Uganda"),
1534
1535
  DocCfg(id='027074', author=FEMALE_HEALTH_COMPANY, description=f"pitch deck (USAID was a customer)"),
1535
1536
  DocCfg(id='032735', author=GORDON_GETTY, description=f"on Trump", date='2018-03-20'), # Dated based on concurrent emails from Getty
1536
- DocCfg(id='025540', author=JEFFREY_EPSTEIN, description=f"rough draft of Epstein's side of the story?"),
1537
+ DocCfg(id='025540', author=JEFFREY_EPSTEIN, description=f"rough draft of his side of the story"),
1537
1538
  DocCfg(id='026634', author='Michael Carrier', description=f"comments about an Apollo linked hedge fund 'DE Fund VIII'"),
1538
1539
  DocCfg(id='031425', author=SCOTT_J_LINK, description=f'completely redacted email from', is_interesting=False),
1539
1540
  DocCfg(id='020447', author='Working Group on Chinese Influence Activities in the U.S.', description=f'Promoting Constructive Vigilance'),
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.json_metadata or args.colors_only
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:
@@ -15,7 +15,7 @@ from epstein_files.util.data import extract_last_name, listify, without_falsey
15
15
  CIVIL_ATTORNEY = 'civil attorney'
16
16
  CRIMINAL_DEFENSE_ATTORNEY = 'criminal defense attorney'
17
17
  CRIMINAL_DEFENSE_2008 = f"{CRIMINAL_DEFENSE_ATTORNEY} on 2008 case"
18
- EPSTEIN_LAWYER = 'epstein_lawyer'
18
+ EPSTEIN_LAWYER = 'Epstein lawyer'
19
19
  EPSTEIN_V_ROTHSTEIN_EDWARDS_ATTORNEY = f"{CIVIL_ATTORNEY} working on {EPSTEIN_V_ROTHSTEIN_EDWARDS}"
20
20
  ESTATE_EXECUTOR = 'estate executor'
21
21
  EPSTEIN_ESTATE_EXECUTOR = f"Epstein {ESTATE_EXECUTOR}"
@@ -103,7 +103,7 @@ class HighlightedNames(HighlightedText):
103
103
  def get_info(self, name: str) -> str | None:
104
104
  """Label and additional info for 'name' if 'name' is in self.emailers."""
105
105
  info_pieces = [
106
- None if len(self.emailers) == 1 else (self.category or self.label.title()),
106
+ None if len(self.emailers) == 1 else (self.category or self.label.replace('_', ' ')),
107
107
  self.emailers.get(name),
108
108
  ]
109
109
 
@@ -111,7 +111,7 @@ class HighlightedNames(HighlightedText):
111
111
  return ', '.join(info_pieces) if info_pieces else None
112
112
 
113
113
  def _emailer_pattern(self, name: str) -> str:
114
- """Pattern matching 'name'. Extends value in EMAILER_ID_REGEXES with last name if it exists."""
114
+ """Pattern matching 'name'. Extends value in EMAILER_ID_REGEXES with first/last name if it exists."""
115
115
  name = remove_question_marks(name)
116
116
  last_name = extract_last_name(name)
117
117
  first_name = name.removesuffix(f" {last_name}")
@@ -141,11 +141,12 @@ class HighlightedNames(HighlightedText):
141
141
 
142
142
  HIGHLIGHTED_NAMES = [
143
143
  HighlightedNames(
144
- label='africa',
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|(jeffrey\s+)?wernick|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
+ 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
  },
@@ -183,12 +185,15 @@ HIGHLIGHTED_NAMES = [
183
185
  pattern=r"CBD|cannabis|marijuana|THC|WEED(guide|maps)?[^s]?",
184
186
  ),
185
187
  HighlightedNames(
186
- label='china',
188
+ label='China',
187
189
  style='bright_red',
188
- pattern=r"Ali.?baba|Beijing|CCP|Chin(a|e?se)(?! Daily)|DPRK|Gino\s+Yu|Global Times|Guo|Hong|Huaw[ae]i|Kim\s*Jong\s*Un|Kong|Jack\s+Ma|Kwok|Ministry\sof\sState\sSecurity|Mongolian?|MSS|North\s*Korea|Peking|PRC|SCMP|Tai(pei|wan)|Xi(aomi)?|Jinping",
190
+ pattern=r"Ali.?baba|Beijing|CCP|Chin(a|e?se)(?! Daily)|DPRK|Global Times|Guo|Hong|Huaw[ae]i|Kim\s*Jong\s*Un|Kong|Jack\s+Ma|Kwok|Ministry\sof\sState\sSecurity|Mongolian?|MSS|North\s*Korea|Peking|PRC|SCMP|Tai(pei|wan)|Xi(aomi)?|Jinping",
191
+ emailers={
192
+ 'Gino Yu': 'professor / game designer in Hong Kong',
193
+ }
189
194
  ),
190
195
  HighlightedNames(
191
- label='deepak_chopra',
196
+ label='Deepak Chopra',
192
197
  style='dark_sea_green4',
193
198
  emailers = {
194
199
  'Carolyn Rangel': 'assistant',
@@ -196,7 +201,7 @@ HIGHLIGHTED_NAMES = [
196
201
  }
197
202
  ),
198
203
  HighlightedNames(
199
- label='democrats',
204
+ label='Democrats',
200
205
  style='sky_blue1',
201
206
  pattern=r'(Al\s*)?Franken|((Bill|Hillart?y)\s*)?Clinton|((Chuck|Charles)\s*)?S(ch|hc)umer|(Diana\s*)?DeGette|DNC|Elena\s*Kagan|(Eliott?\s*)?Spitzer(, Eliot)?|George\s*Mitchell|(George\s*)?Soros|Hill?ary|Dem(ocrat(ic)?)?|(Jo(e|seph)\s*)?Biden|(John\s*)?Kerry|Lisa Monaco|(Matteo\s*)?Salvini|Maxine\s*Waters|(Barac?k )?Obama|(Nancy )?Pelosi|Ron\s*Dellums|Schumer|(Tim\s*)?Geithner|Vernon\s*Jordan',
202
207
  ),
@@ -271,7 +276,7 @@ HIGHLIGHTED_NAMES = [
271
276
  HighlightedNames(
272
277
  label=ESTATE_EXECUTOR,
273
278
  style='purple3 bold',
274
- category='epstein lawyer',
279
+ category='Epstein lawyer',
275
280
  emailers = {
276
281
  DARREN_INDYKE: EPSTEIN_ESTATE_EXECUTOR,
277
282
  RICHARD_KAHN: EPSTEIN_ESTATE_EXECUTOR,
@@ -280,7 +285,7 @@ HIGHLIGHTED_NAMES = [
280
285
  HighlightedNames(
281
286
  label='europe',
282
287
  style='light_sky_blue3',
283
- pattern=r'(Angela )?Merk(el|le)|Austria|(Benjamin\s*)?Harnwell|Berlin|Borge|Boris\s*Johnson|Brexit(eers?)?|Brit(ain|ish)|Brussels|Cannes|(Caroline|Jack)?\s*Lang(, Caroline)?|Cypr(iot|us)|Davos|ECB|England|EU|Europe(an)?(\s*Union)?|Fr(ance|ench)|Geneva|Germany?|Gillard|Gree(ce|k)|Ital(ian|y)|Jacques|(Kevin\s*)?Rudd|Le\s*Pen|London|Macron|Melusine|Munich|(Natalia\s*)?Veselnitskaya|(Nicholas\s*)?Sarkozy|Nigel(\s*Farage)?|Norw(ay|egian)|Oslo|Paris|Polish|(Sebastian )?Kurz|(Vi(c|k)tor\s+)?Orbah?n|Edward Rod Larsen|Strasbourg|Strauss[- ]?Kahn|Swed(en|ish)(?![-\s]+America)|Switzerland|(Tony\s)?Blair|U\.?K\.?|Ukrain(e|ian)|Vienna|(Vitaly\s*)?Churkin|Zug',
288
+ pattern=r'(Angela )?Merk(el|le)|Austria|(Benjamin\s*)?Harnwell|Berlin|Borge|Boris\s*Johnson|Brexit(eers?)?|Brit(ain|ish)|Brussels|Cannes|(Caroline|Jack)?\s*Lang(, Caroline)?|Cypr(iot|us)|Davos|ECB|England|EU|Europe(an)?(\s*Union)?|Fr(ance|ench)|Geneva|Germany?|Gillard|Gree(ce|k)|Ital(ian|y)|Jacques|(Kevin\s*)?Rudd|Le\s*Pen|London|Macron|Melusine|Munich|(Natalia\s*)?Veselnitskaya|(Nicholas\s*)?Sarkozy|Nigel(\s*Farage)?|Norw(ay|egian)|Oslo|Paris|Polish|pope|(Sebastian )?Kurz|(Vi(c|k)tor\s+)?Orbah?n|Edward Rod Larsen|Strasbourg|Strauss[- ]?Kahn|Swed(en|ish)(?![-\s]+America)|Switzerland|(Tony\s)?Blair|U\.?K\.?|Ukrain(e|ian)|Vienna|(Vitaly\s*)?Churkin|Zug',
284
289
  emailers = {
285
290
  ANDRZEJ_DUDA: 'former president of Poland',
286
291
  MIROSLAV_LAJCAK: 'Russia-friendly Slovakian politician, friend of Steve Bannon',
@@ -290,9 +295,8 @@ HIGHLIGHTED_NAMES = [
290
295
  }
291
296
  ),
292
297
  HighlightedNames(
293
- label='famous_lawyer',
298
+ label='famous lawyer',
294
299
  style='medium_purple3',
295
- category='famous_lawyer',
296
300
  pattern=r'(David\s*)?Bo[il]es|dersh|(Gloria\s*)?Allred|(Mi(chael|ke)\s*)?Avenatti',
297
301
  emailers = {
298
302
  ALAN_DERSHOWITZ: 'Harvard Law School professor and all around (in)famous American lawyer',
@@ -316,7 +320,8 @@ HIGHLIGHTED_NAMES = [
316
320
  MELANIE_SPINELLA: f'representative of {LEON_BLACK}',
317
321
  MORTIMER_ZUCKERMAN: 'business partner of Epstein',
318
322
  PAUL_BARRETT: None,
319
- PAUL_MORRIS: 'Deutsche Bank',
323
+ PAUL_MORRIS: DEUTSCHE_BANK,
324
+ 'Steven Elkman': DEUTSCHE_BANK,
320
325
  }
321
326
  ),
322
327
  HighlightedNames(
@@ -334,7 +339,7 @@ HIGHLIGHTED_NAMES = [
334
339
  },
335
340
  ),
336
341
  HighlightedNames(
337
- label=HARVARD.lower(),
342
+ label=HARVARD,
338
343
  style='light_goldenrod3',
339
344
  pattern=r'Cambridge|(Derek\s*)?Bok|Elisa(\s*New)?|Harvard(\s*(Business|Law|University)(\s*School)?)?|(Jonathan\s*)?Zittrain|(Stephen\s*)?Kosslyn',
340
345
  emailers = {
@@ -349,9 +354,9 @@ HIGHLIGHTED_NAMES = [
349
354
  }
350
355
  ),
351
356
  HighlightedNames(
352
- label='india',
357
+ label='India',
353
358
  style='bright_green',
354
- 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',
355
360
  emailers = {
356
361
  ANIL_AMBANI: 'chairman of Reliance Group',
357
362
  VINIT_SAHNI: None,
@@ -359,7 +364,7 @@ HIGHLIGHTED_NAMES = [
359
364
  }
360
365
  ),
361
366
  HighlightedNames(
362
- label='israel',
367
+ label='Israel',
363
368
  style='dodger_blue2',
364
369
  pattern=r"AIPAC|Bibi|(eh|(Ehud|Nili Priell) )?barak|Ehud\s*Barack|Israeli?|Jerusalem|J\s*Street|Mossad|Netanyahu|(Sheldon\s*)?Adelson|Tel\s*Aviv|(The\s*)?Shimon\s*Post|Yitzhak|Rabin|YIVO|zionist",
365
370
  emailers={
@@ -369,7 +374,7 @@ HIGHLIGHTED_NAMES = [
369
374
  }
370
375
  ),
371
376
  HighlightedNames(
372
- label='japan',
377
+ label='Japan',
373
378
  style='color(168)',
374
379
  pattern=r'BOJ|(Bank\s+of\s+)?Japan(ese)?|jpy?(?! Morgan)|SG|Singapore|Toky[op]',
375
380
  ),
@@ -389,7 +394,7 @@ HIGHLIGHTED_NAMES = [
389
394
  }
390
395
  ),
391
396
  HighlightedNames(
392
- label='latin america',
397
+ label='Latin America',
393
398
  style='yellow',
394
399
  pattern=r'Argentin(a|ian)|Bolsonar[aio]|Bra[sz]il(ian)?|Bukele|Caracas|Castro|Colombian?|Cuban?|El\s*Salvador|((Enrique )?Pena )?Nieto|LatAm|Lula|Mexic(an|o)|(Nicolas\s+)?Maduro|Panama( Papers)?|Peru|Venezuelan?|Zambrano',
395
400
  ),
@@ -419,8 +424,8 @@ HIGHLIGHTED_NAMES = [
419
424
  HighlightedNames(
420
425
  label='mideast',
421
426
  style='dark_sea_green4',
422
- # this won't match ever because of word boundary: [-\s]9/11[\s.]
423
- 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|Dubai|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)|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
+ # something like this won't match ever because of word boundary: [-\s]9/11[\s.]
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?)",
424
429
  emailers = {
425
430
  ANAS_ALRASHEED: f'former information minister of Kuwait {QUESTION_MARKS}',
426
431
  AZIZA_ALAHMADI: 'Abu Dhabi Department of Culture & Tourism',
@@ -463,7 +468,7 @@ HIGHLIGHTED_NAMES = [
463
468
  }
464
469
  ),
465
470
  HighlightedNames(
466
- label='republicans',
471
+ label='Republicans',
467
472
  style='bold dark_red',
468
473
  pattern=r'Alberto\sGonzale[sz]|(Alex\s*)?Acosta|(Bill\s*)?Barr|Bill\s*Shine|(Bob\s*)?Corker|(John\s*(R.?\s*)?)Bolton|Broidy|(Chris\s)?Christie|Devin\s*Nunes|(Don\s*)?McGa[hn]n|McMaster|(George\s*)?Nader|GOP|(Brett\s*)?Kavanaugh|Kissinger|Kobach|Koch\s*Brothers|Kolfage|Kudlow|Lewandowski|(Marco\s)?Rubio|(Mark\s*)Meadows|Mattis|McCain|(?<!Merwin Dela )Cruz|(Michael\s)?Hayden|((General|Mike)\s*)?(Flynn|Pence)|(Mitt\s*)?Romney|Mnuchin|Nikki|Haley|(Paul\s+)?(Manafort|Volcker)|(Peter\s)?Navarro|Pompeo|Reagan|Reince|Priebus|Republican|(Rex\s*)?Tillerson|(?<!Cynthia )(Richard\s*)?Nixon|Sasse|Tea\s*Party',
469
474
  # There's no emails from these people, they're just here to automate the regex creation for both first + last names
@@ -481,7 +486,7 @@ HIGHLIGHTED_NAMES = [
481
486
  },
482
487
  ),
483
488
  HighlightedNames(
484
- label='russia',
489
+ label='Russia',
485
490
  style='red bold',
486
491
  pattern=r'Alfa\s*Bank|Anya\s*Rasulova|Chernobyl|Day\s+One\s+Ventures|(Dmitry\s)?(Kiselyov|(Lana\s*)?Pozhidaeva|Medvedev|Rybolo(o?l?ev|vlev))|Dmitry|FSB|GRU|KGB|Kislyak|Kremlin|Kuznetsova|Lavrov|Lukoil|Moscow|(Oleg\s*)?Deripaska|Oleksandr Vilkul|Rosneft|RT|St.?\s*?Petersburg|Russian?|Sberbank|Soviet(\s*Union)?|USSR|Vladimir|(Vladimir\s*)?(Putin|Yudashkin)|Women\s*Empowerment|Xitrans',
487
492
  emailers = {
@@ -511,7 +516,7 @@ HIGHLIGHTED_NAMES = [
511
516
  },
512
517
  ),
513
518
  HighlightedNames(
514
- label='southeast_asia',
519
+ label='southeast Asia',
515
520
  style='light_salmon3 bold',
516
521
  pattern=r'Bangkok|Burm(a|ese)|Cambodian?|Laos|Malaysian?|Myan?mar|Thai(land)?|Vietnam(ese)?',
517
522
  ),
@@ -541,7 +546,7 @@ HIGHLIGHTED_NAMES = [
541
546
  pattern=r'BVI|(Jane|Tiffany)\s*Doe|Katie\s*Johnson|(Virginia\s+((L\.?|Roberts)\s+)?)?Giuffre|Virginia\s+Roberts',
542
547
  ),
543
548
  HighlightedNames(
544
- label='victim_lawyer',
549
+ label='victim lawyer',
545
550
  style='dark_magenta bold',
546
551
  pattern=r'(Alan(\s*P.)?|MINTZ)\s*FRAADE|Paul\s*(G.\s*)?Cassell|Rothstein\s*Rosenfeldt\s*Adler|(Scott\s*)?Rothstein|(J\.?\s*)?(Stan(ley)?\s*)?Pottinger',
547
552
  emailers = {
@@ -554,7 +559,7 @@ HIGHLIGHTED_NAMES = [
554
559
  style='sea_green1',
555
560
  pattern=r'Antigua|Bahamas|Caribb?ean|Dominican\s*Republic|(Great|Little)\s*St.?\s*James|Haiti(an)?|(John\s*)deJongh(\s*Jr\.?)|(Kenneth E\. )?Mapp|Palm\s*Beach(?!\s*Post)|PBI|S(ain)?t.?\s*Thomas|USVI|(?<!Epstein )VI|(The\s*)?Virgin\s*Islands(\s*Daily\s*News)?', # TODO: VI Daily News should be yellow but it's hard bc Daily News xists
556
561
  emailers = {
557
- CECILE_DE_JONGH: f'First lady 2007-2015',
562
+ CECILE_DE_JONGH: f'first lady 2007-2015',
558
563
  STACEY_PLASKETT: 'non-voting member of Congress',
559
564
  KENNETH_E_MAPP: 'Governor',
560
565
  },
@@ -573,6 +578,9 @@ HIGHLIGHTED_NAMES = [
573
578
  label=STEVE_BANNON,
574
579
  style='color(58)',
575
580
  pattern=r'((Steve|Sean)\s*)?Bannon?|(American\s*)?Dharma',
581
+ emailers = {
582
+ STEVE_BANNON: 'Trump campaign manager, memecoin grifter, convicted criminal',
583
+ }
576
584
  ),
577
585
  HighlightedNames(
578
586
  emailers={STEVEN_HOFFENBERG: HEADER_ABBREVIATIONS['Hoffenberg']},
@@ -646,7 +654,7 @@ ALL_HIGHLIGHTS = HIGHLIGHTED_NAMES + HIGHLIGHTED_TEXTS
646
654
 
647
655
 
648
656
  class EpsteinHighlighter(RegexHighlighter):
649
- """rich.highlighter that finds and colors interesting keywords based on the above config."""
657
+ """Finds and colors interesting keywords based on the above config."""
650
658
  base_style = f"{REGEX_STYLE_PREFIX}."
651
659
  highlights = [highlight_group.regex for highlight_group in ALL_HIGHLIGHTS]
652
660
 
@@ -666,10 +674,8 @@ def get_style_for_category(category: str) -> str | None:
666
674
  elif category == SOCIAL:
667
675
  return get_style_for_category(PUBLICIST)
668
676
 
669
- category = CATEGORY_STYLE_MAPPING.get(category, category)
670
-
671
677
  for highlight_group in HIGHLIGHTED_NAMES:
672
- if highlight_group.label == category:
678
+ if highlight_group.label == CATEGORY_STYLE_MAPPING.get(category, category):
673
679
  return highlight_group.style
674
680
 
675
681
 
@@ -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,13 +29,12 @@ 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,
35
37
  STEVE_BANNON,
36
- PRINCE_ANDREW,
37
38
  JIDE_ZEITLIN,
38
39
  DAVID_STERN,
39
40
  MOHAMED_WAHEED_HASSAN,
@@ -47,9 +48,8 @@ DEFAULT_EMAILERS = [
47
48
  # Order matters. Default names to print tables w/email subject, timestamp, etc for. # TODO: get rid of this ?
48
49
  DEFAULT_EMAILER_TABLES: list[str | None] = [
49
50
  GHISLAINE_MAXWELL,
50
- LEON_BLACK,
51
+ PRINCE_ANDREW,
51
52
  SULTAN_BIN_SULAYEM,
52
- DEEPAK_CHOPRA,
53
53
  ARIANE_DE_ROTHSCHILD,
54
54
  ]
55
55
 
@@ -60,7 +60,7 @@ if len(set(DEFAULT_EMAILERS).intersection(set(DEFAULT_EMAILER_TABLES))) > 0:
60
60
  def print_emails_section(epstein_files: EpsteinFiles) -> list[Email]:
61
61
  """Returns emails that were printed (may contain dupes if printed for both author and recipient)."""
62
62
  print_section_header(('Selections from ' if not args.all_emails else '') + 'His Emails')
63
- print_all_files_page_link(epstein_files)
63
+ print_other_page_link(epstein_files)
64
64
  emailers_to_print: list[str | None]
65
65
  emailer_tables: list[str | None] = []
66
66
  already_printed_emails: list[Email] = []
@@ -94,7 +94,7 @@ def print_emails_section(epstein_files: EpsteinFiles) -> list[Email]:
94
94
  num_emails_printed_since_last_color_key = 0
95
95
 
96
96
  if emailer_tables:
97
- print_author_header(f"Email Tables for {len(emailer_tables)} Other People", 'white')
97
+ print_author_panel(f"Email Tables for {len(emailer_tables)} Other People", 'white')
98
98
 
99
99
  for name in DEFAULT_EMAILER_TABLES:
100
100
  epstein_files.print_emails_table_for(name)
@@ -148,7 +148,7 @@ def print_other_files_section(files: list[OtherFile], epstein_files: EpsteinFile
148
148
  if args.all_other_files:
149
149
  console.line(1)
150
150
  else:
151
- print_all_files_page_link(epstein_files)
151
+ print_other_page_link(epstein_files)
152
152
  console.line(2)
153
153
 
154
154
  for table in [category_table, other_files_preview_table]:
@@ -171,6 +171,32 @@ def print_text_messages_section(epstein_files: EpsteinFiles) -> None:
171
171
  print_centered(MessengerLog.summary_table(epstein_files.imessage_logs))
172
172
 
173
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
+
174
200
  def write_json_metadata(epstein_files: EpsteinFiles) -> None:
175
201
  json_str = epstein_files.json_metadata()
176
202
 
@@ -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.output_files import SITE_URLS
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,14 +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 print_all_files_page_link(epstein_files: 'EpsteinFiles') -> None:
147
- markup_msg = link_markup(SITE_URLS[EMAIL], 'the other page', style='light_slate_grey bold')
148
- txt = Text.from_markup(markup_msg).append(f' is uncurated and has all {len(epstein_files.other_files)}')
149
- txt.append(f" unclassifiable files and {len(epstein_files.emails):,} emails")
150
- print_centered(parenthesize(txt), style='dim')
151
-
152
-
153
- def print_author_header(msg: str, color: str | None, footer: str | None = None) -> None:
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."""
154
147
  txt = Text(msg, justify='center')
155
148
  color = color or 'white'
156
149
  color = 'white' if color == DEFAULT else color
@@ -187,28 +180,32 @@ def print_color_key() -> None:
187
180
  print_centered(vertically_pad(color_table))
188
181
 
189
182
 
190
- def print_header(epstein_files: 'EpsteinFiles') -> None:
191
- not_optimized_msg = f"This site isn't optimized for mobile"
183
+ def print_title_page_header(epstein_files: 'EpsteinFiles') -> None:
184
+ not_optimized_msg = f"This page isn't optimized for mobile"
192
185
 
193
186
  if not args.all_emails:
194
187
  not_optimized_msg += f" but if you get past the header it should be readable"
195
188
 
196
189
  console.print(f"{not_optimized_msg}.\n", style='dim')
197
190
  print_page_title(width=TITLE_WIDTH)
198
- site_type: SiteType = EMAIL if args.all_emails else TEXT_MESSAGE
199
- print_starred_header(f"This is the Epstein {site_type.title()}s site", num_spaces=4, num_stars=14)
200
- other_site_type: SiteType = TEXT_MESSAGE if site_type == EMAIL else EMAIL
201
- other_site_msg = "another site with" + (' all of' if other_site_type == EMAIL else '')
202
- other_site_msg += f" Epstein's {other_site_type}s also generated by this code"
203
-
204
- markup_msg = link_markup(SITE_URLS[other_site_type], other_site_msg, OTHER_SITE_LINK_STYLE)
205
- print_centered(parenthesize(Text.from_markup(markup_msg)), style='bold')
206
- word_count_link = link_text_obj(WORD_COUNT_URL, 'most frequently used words in the emails and texts', AUX_SITE_LINK_STYLE)
207
- print_centered(parenthesize(word_count_link))
208
- metadata_link = link_text_obj(JSON_METADATA_URL, 'author attribution explanations', AUX_SITE_LINK_STYLE)
209
- print_centered(parenthesize(metadata_link))
210
- json_link = link_text_obj(WORD_COUNT_URL, "epstein's json files", AUX_SITE_LINK_STYLE)
211
- print_centered(parenthesize(json_link))
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:
212
209
  _print_external_links()
213
210
  console.line()
214
211
  _print_abbreviations_table()
@@ -272,6 +269,23 @@ def print_numbered_list_of_emailers(_list: list[str | None], epstein_files = Non
272
269
  console.line()
273
270
 
274
271
 
272
+ def print_other_page_link(epstein_files: 'EpsteinFiles') -> None:
273
+ markup_msg = link_markup(other_site_url(), 'the other page', style='light_slate_grey bold')
274
+
275
+ if other_site_type() == EMAIL:
276
+ txt = Text.from_markup(markup_msg).append(f' is uncurated and has all {len(epstein_files.other_files)}')
277
+ txt.append(f" unclassifiable files and {len(epstein_files.emails):,} emails")
278
+ else:
279
+ txt = Text.from_markup(markup_msg).append(f' displays only a small collection of emails and')
280
+ txt.append(" unclassifiable files of particular interest")
281
+
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')
287
+
288
+
275
289
  def print_page_title(expand: bool = True, width: int | None = None) -> None:
276
290
  title_panel = Panel(Text(PAGE_TITLE, justify='center'), expand=expand, style=TITLE_STYLE, width=width)
277
291
  console.print(Align.center(vertically_pad(title_panel)))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: epstein-files
3
- Version: 1.0.15
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
@@ -0,0 +1,33 @@
1
+ epstein_files/__init__.py,sha256=Roo3XeVQXkgMOQMdq74-dBdkRGeY3sAv7LihZwaGvaY,5538
2
+ epstein_files/documents/communication.py,sha256=oqNsSDWe-N0jSmchIHxpihihzIWha-foFqMwKZlxyng,2057
3
+ epstein_files/documents/document.py,sha256=s77ZQwwYmm-PDc2rZqg01FY4hpBh-XF5OHcgTVZZBes,17395
4
+ epstein_files/documents/email.py,sha256=rGnJjRzLnq70jZpWU3oslNW-FhztlcszS3v7mwEcmzY,41222
5
+ epstein_files/documents/emails/email_header.py,sha256=wkPfSLbmzkAeQwvhf0bAeFDLPbQT-EeG0v8vNNLYktM,7502
6
+ epstein_files/documents/imessage/text_message.py,sha256=icIiKRRuZapkV9r_PID_7hEfy7YvPrIm9Emc4QiYxbw,2806
7
+ epstein_files/documents/json_file.py,sha256=WcZW5NNqA67rHTdopbOGtup00muNaLlvrNgKb-K4zO8,1504
8
+ epstein_files/documents/messenger_log.py,sha256=zRVVe82cNmNmg50kqScH38yGGoSx8NjCWfLwFGUb0rs,6501
9
+ epstein_files/documents/other_file.py,sha256=CCwOYsipYTWZj8pSTl0kgUy_LRu7Z5ZuWygQEYhilNA,9778
10
+ epstein_files/epstein_files.py,sha256=LsvD9O1YNp9xFIVOrswOfnbfafxgbS9ve9cqSfQENy8,17881
11
+ epstein_files/util/constant/common_words.py,sha256=aR0UjoWmxyR49XS-DtHECQ1CiA_bK8hNP6CQ1TS9yZA,3696
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
+ epstein_files/util/constant/strings.py,sha256=rOKmYrGIZFPQbhCgd8z7mUF0CbQQBO4ij8kXvGTDkoE,1857
16
+ epstein_files/util/constant/urls.py,sha256=-cylNL7xJi18a6fawVTF8MO3oEdjMvo9Hg1MwC-7ydI,5098
17
+ epstein_files/util/constants.py,sha256=4WABkEBshduSV4cZkxv0MwgJci9g6MxjhtJfPjersEo,112495
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=AWZYDHQ6JRHobY1o7BHRbVObYpBejhsUN4TrOyZbzgQ,5783
21
+ epstein_files/util/file_helper.py,sha256=uJzNHihuAi2-XMNbUdjdovgppcIt-fVIIrXHXVShA6Q,2904
22
+ epstein_files/util/highlighted_group.py,sha256=4r-asWz53zVHKckONxTOluFCRqbX92nAID1gdXqf08g,37047
23
+ epstein_files/util/logging.py,sha256=fuREq06xUUI3DfCV2JE-8QM-sQKxpLDj0_AYFO6qR1M,1983
24
+ epstein_files/util/output.py,sha256=WK37ZbTvhPHczH8GNly2Pjw9IwAsiF_yn43u7Q2jf7k,10051
25
+ epstein_files/util/rich.py,sha256=oic1wcqipTSj9G1YYP8HTzsJvGeIgrK8vN95uGOJ8sU,15715
26
+ epstein_files/util/search_result.py,sha256=1fxe0KPBQXBk4dLfu6m0QXIzYfZCzvaSkWqvghJGzxY,567
27
+ epstein_files/util/timer.py,sha256=8hxW4Y1JcTUfnBrHh7sL2pM9xu1sL4HFQM4CmmzTarU,837
28
+ epstein_files/util/word_count.py,sha256=o_-HnfzHdPDPR8oA_dv6fjy1dbsHee8p_aoe62PEQHw,9213
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,,
@@ -1,33 +0,0 @@
1
- epstein_files/__init__.py,sha256=nOJ26LghS0sCdQ4qGwklVm18kbuFRn6Uu1-JDE8jwd4,5050
2
- epstein_files/documents/communication.py,sha256=oqNsSDWe-N0jSmchIHxpihihzIWha-foFqMwKZlxyng,2057
3
- epstein_files/documents/document.py,sha256=5GdM8grhaaefmXeV26XwYRZvMNqFETFnE7mFpVGGNik,17249
4
- epstein_files/documents/email.py,sha256=h8V-20JAw2U_7mIWfnt3phvnykf-ioCdML8dAYoFVMc,40937
5
- epstein_files/documents/emails/email_header.py,sha256=wkPfSLbmzkAeQwvhf0bAeFDLPbQT-EeG0v8vNNLYktM,7502
6
- epstein_files/documents/imessage/text_message.py,sha256=JPHlO-5Xgt5V5E9fDsgCEr3XOp7FyzlhIHmZhFNZcmg,2806
7
- epstein_files/documents/json_file.py,sha256=WcZW5NNqA67rHTdopbOGtup00muNaLlvrNgKb-K4zO8,1504
8
- epstein_files/documents/messenger_log.py,sha256=yFw5ScyP3KM8AoV9D36GUxLIT3pLsDdpi5F61Dw7fwQ,6510
9
- epstein_files/documents/other_file.py,sha256=CCwOYsipYTWZj8pSTl0kgUy_LRu7Z5ZuWygQEYhilNA,9778
10
- epstein_files/epstein_files.py,sha256=pBtrcEGZAMUDO_Xghxt-bd_RZp6JQlDj770EaM9oEZ4,17887
11
- epstein_files/util/constant/common_words.py,sha256=aR0UjoWmxyR49XS-DtHECQ1CiA_bK8hNP6CQ1TS9yZA,3696
12
- epstein_files/util/constant/html.py,sha256=9U098TGzlghGg4WfxLYHyub5JGR17Dv7VP5i2MSu8Kk,1415
13
- epstein_files/util/constant/names.py,sha256=nk_kuOtIo_UTl4R4klUGJRfCRNDZZkqKkjbJx6XCOzQ,10471
14
- epstein_files/util/constant/output_files.py,sha256=BkV4_gmdj46RfGy5SFYp6dgTty3FtlBth5YGmaGutls,1700
15
- epstein_files/util/constant/strings.py,sha256=rOKmYrGIZFPQbhCgd8z7mUF0CbQQBO4ij8kXvGTDkoE,1857
16
- epstein_files/util/constant/urls.py,sha256=Rpr9QQfjk2-8YGTXSQYg2oNA2ZxWrkedr5tL_53Z6ak,4896
17
- epstein_files/util/constants.py,sha256=cIFDxq7-t6UE_qs2RtcyY2R3BAZmVV3glgTgKVkrG0c,112348
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=aSUC_TwdtKB8z6PCDaLeu3k5qZE3AgcsP9jU2p9eaP0,5640
21
- epstein_files/util/file_helper.py,sha256=uJzNHihuAi2-XMNbUdjdovgppcIt-fVIIrXHXVShA6Q,2904
22
- epstein_files/util/highlighted_group.py,sha256=ACDwLratRV9bemDyHY528JMFDf0ZDjm9HBfFJiqDgyY,36535
23
- epstein_files/util/logging.py,sha256=fuREq06xUUI3DfCV2JE-8QM-sQKxpLDj0_AYFO6qR1M,1983
24
- epstein_files/util/output.py,sha256=2YKpL2gGhS2SI5nC2yan0vNHvYMg1Ayq_ptzx9Gi9E4,9055
25
- epstein_files/util/rich.py,sha256=bQPlOEZ6qTpVF4yPnwmlBk4K11zH5RJjwbWeBIOUUGE,15191
26
- epstein_files/util/search_result.py,sha256=1fxe0KPBQXBk4dLfu6m0QXIzYfZCzvaSkWqvghJGzxY,567
27
- epstein_files/util/timer.py,sha256=8hxW4Y1JcTUfnBrHh7sL2pM9xu1sL4HFQM4CmmzTarU,837
28
- epstein_files/util/word_count.py,sha256=o_-HnfzHdPDPR8oA_dv6fjy1dbsHee8p_aoe62PEQHw,9213
29
- epstein_files-1.0.15.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
30
- epstein_files-1.0.15.dist-info/METADATA,sha256=yELMsoWwHhLiDjU7OFOYDAmifBak0h6xGLEu-3nwiHc,5867
31
- epstein_files-1.0.15.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
32
- epstein_files-1.0.15.dist-info/entry_points.txt,sha256=5qYgwAXpxegeAicD_rzda_trDRnUC51F5UVDpcZ7j6Q,240
33
- epstein_files-1.0.15.dist-info/RECORD,,