optimuslib 0.0.47__py3-none-any.whl → 0.0.48__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.
optimuslib/optimuslib.py CHANGED
@@ -109,7 +109,7 @@ log.setLevel(logging.DEBUG)
109
109
  # # Handler - 1
110
110
  file = logging.FileHandler('optimuslibLogs.log', 'a', 'utf-8')
111
111
  # fileformat = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(filename)s - %(module)s:%(funcName)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
112
- fileformat = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(filename)s - %(funcName)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
112
+ fileformat = logging.Formatter(fmt='%(asctime)s | %(levelname)s | %(filename)s | %(funcName)s | %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
113
113
  file.setLevel(logging.DEBUG)
114
114
  # file.setLevel(logging.INFO)
115
115
  file.setFormatter(fileformat)
@@ -1552,21 +1552,43 @@ from datetime import datetime, timezone
1552
1552
 
1553
1553
 
1554
1554
  def extract_clean_text(msg):
1555
- """Extract readable text from HTML email"""
1556
- text = ""
1555
+ text_parts = []
1556
+
1557
1557
  for part in msg.walk():
1558
- if part.get_content_type() == "text/html":
1558
+ content_type = part.get_content_type()
1559
+ charset = part.get_content_charset()
1560
+
1561
+ log.debug(f"Found MIME part: {content_type}, charset={charset}")
1562
+
1563
+ if content_type in ("text/plain", "text/html"):
1559
1564
  raw = part.get_payload(decode=True)
1560
- decoded = quopri.decodestring(raw).decode(errors="ignore")
1561
- soup = BeautifulSoup(decoded, "html.parser")
1562
- text += soup.get_text(" ")
1563
- return text
1564
1565
 
1566
+ if not raw:
1567
+ log.debug("Empty payload")
1568
+ continue
1569
+
1570
+ try:
1571
+ decoded = raw.decode(charset or "utf-8", errors="ignore")
1572
+ except Exception:
1573
+ decoded = quopri.decodestring(raw).decode(errors="ignore")
1574
+
1575
+ if content_type == "text/html":
1576
+ soup = BeautifulSoup(decoded, "html.parser")
1577
+ decoded = soup.get_text(" ")
1565
1578
 
1566
- def is_recent(msg):
1567
- """Check if email was received within MAX_AGE_SECONDS"""
1579
+ text_parts.append(decoded)
1580
+
1581
+ full_text = " ".join(text_parts)
1582
+ log.debug(f"Extracted email text (first 300 chars): {full_text[:300]!r}")
1583
+
1584
+ return full_text
1585
+
1586
+
1587
+
1588
+ def is_recent(msg, max_age_seconds):
1568
1589
  date_hdr = msg.get("Date")
1569
1590
  if not date_hdr:
1591
+ log.debug("Email has no Date header")
1570
1592
  return False
1571
1593
 
1572
1594
  try:
@@ -1576,10 +1598,13 @@ def is_recent(msg):
1576
1598
 
1577
1599
  now = datetime.now(timezone.utc)
1578
1600
  age = (now - msg_time).total_seconds()
1579
- return 0 <= age <= MAX_AGE_SECONDS
1580
- except Exception:
1581
- return False
1582
1601
 
1602
+ log.debug(f"Email age (seconds): {age}")
1603
+ return 0 <= age <= max_age_seconds
1604
+
1605
+ except Exception as e:
1606
+ log.exception("Failed to parse email date")
1607
+ return False
1583
1608
 
1584
1609
  def input_with_timeout(prompt, timeout):
1585
1610
  """Get user input with timeout"""
@@ -1612,28 +1637,46 @@ def poll_for_otp(EMAIL, APP_PASSWORD, SENDER, SUBJECT, POLL_INTERVAL, POLL_TIMEO
1612
1637
  # 🔑 Gmail IMAP requires re-select to refresh UNSEEN
1613
1638
  mail.select("inbox", readonly=False)
1614
1639
 
1615
- status, messages = mail.search(
1616
- None,
1617
- f'(UNSEEN FROM "{SENDER}" SUBJECT "{SUBJECT}")'
1618
- )
1640
+ filter = f'(UNSEEN FROM "{SENDER}" SUBJECT "{SUBJECT}")'
1641
+ log.debug(f"IMAP search filter: {filter}")
1642
+ status, messages = mail.search(None,filter)
1619
1643
 
1620
1644
  ids = messages[0].split()
1621
1645
 
1646
+ log.debug(f"IMAP search status: {status}")
1647
+ log.debug(f"Message IDs returned: {ids}")
1648
+
1622
1649
  for eid in ids:
1623
1650
  _, data = mail.fetch(eid, "(RFC822)")
1624
1651
  msg = email.message_from_bytes(data[0][1])
1625
1652
 
1626
- if not is_recent(msg):
1653
+ log.debug(f"From: {msg.get('From')}")
1654
+ log.debug(f"Subject: {msg.get('Subject')}")
1655
+ log.debug(f"Date: {msg.get('Date')}")
1656
+
1657
+ if not is_recent(msg, MAX_AGE_SECONDS):
1627
1658
  continue
1628
1659
 
1629
1660
  body = extract_clean_text(msg)
1630
- match = re.search(r"OTP[^0-9]*(\d{6})", body, re.IGNORECASE)
1631
1661
 
1632
- if match:
1633
- # mark email as read
1634
- mail.store(eid, "+FLAGS", "\\Seen")
1635
- mail.logout()
1636
- return match.group(1)
1662
+ if not body.strip():
1663
+ log.warning("Email body extracted but empty")
1664
+ continue
1665
+
1666
+ log.debug("Running OTP regex scan")
1667
+
1668
+ match = re.search(r"\b(\d{4,8})\b", body)
1669
+
1670
+ if not match:
1671
+ log.warning("No OTP match found in email body")
1672
+ continue
1673
+
1674
+ otp = match.group(1)
1675
+ log.info(f"OTP extracted successfully: {otp}")
1676
+
1677
+ mail.store(eid, "+FLAGS", "\\Seen")
1678
+ mail.logout()
1679
+ return otp
1637
1680
 
1638
1681
  log.info("Waiting for OTP...")
1639
1682
  time.sleep(POLL_INTERVAL)
@@ -1643,10 +1686,11 @@ def poll_for_otp(EMAIL, APP_PASSWORD, SENDER, SUBJECT, POLL_INTERVAL, POLL_TIMEO
1643
1686
 
1644
1687
 
1645
1688
  def get_gmail_otp(EMAIL, APP_PASSWORD, SENDER, SUBJECT,POLL_INTERVAL,POLL_TIMEOUT,MAX_AGE_SECONDS,MANUAL_INPUT_TIMEOUT):
1689
+
1646
1690
  """Get OTP automatically or fall back to manual input"""
1647
1691
  try:
1648
1692
  otp = poll_for_otp(EMAIL, APP_PASSWORD, SENDER, SUBJECT, POLL_INTERVAL, POLL_TIMEOUT, MAX_AGE_SECONDS)
1649
- log.info("OTP received automatically:", otp)
1693
+ log.info(f"OTP received automatically: {otp}")
1650
1694
  return otp
1651
1695
 
1652
1696
  except TimeoutError:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: optimuslib
3
- Version: 0.0.47
3
+ Version: 0.0.48
4
4
  Summary: Function Library for mostly used codes
5
5
  Author: Shomi Nanwani
6
6
  Requires-Python: >=3.9,<4.0
@@ -0,0 +1,5 @@
1
+ optimuslib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ optimuslib/optimuslib.py,sha256=xsoQQgTyA5UhIfLS3mEyLKR4jzbhLriT-E-3yk7FcXg,66286
3
+ optimuslib-0.0.48.dist-info/METADATA,sha256=1VrHrlb6_vwzIFBuNiEXSIu1_isvo6Or6tzhtrtt_5A,723
4
+ optimuslib-0.0.48.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
5
+ optimuslib-0.0.48.dist-info/RECORD,,
@@ -1,5 +0,0 @@
1
- optimuslib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- optimuslib/optimuslib.py,sha256=G0k0nO98nOmoG2kVsroA_wDVf7eC-3bTIe8m9LT_Spc,64967
3
- optimuslib-0.0.47.dist-info/METADATA,sha256=ruM-YP1zzXsL3awy5p4au78JdF0QF0CjM7_uoCyZZDo,723
4
- optimuslib-0.0.47.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
5
- optimuslib-0.0.47.dist-info/RECORD,,