optimuslib 0.0.47__py3-none-any.whl → 0.0.49__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
@@ -99,33 +99,27 @@
99
99
  import logging
100
100
 
101
101
  # Creating logger
102
- # logging.basicConfig(level=logging.DEBUG)
103
- # logging.basicConfig(level=logging.DEBUG, filename='logs.txt', format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
104
- # logging.basicConfig(level=logging.DEBUG, filename='logs.txt', format='%(asctime)s — %(name)s — %(levelname)s — %(funcName)s:%(lineno)d — %(message)s', datefmt='%d-%b-%y %H:%M:%S')
105
102
  log = logging.getLogger(__name__)
106
103
  log.setLevel(logging.DEBUG)
107
- # log.setLevel(logging.INFO)
108
104
 
109
- # # Handler - 1
105
+ # Handler - 1: File Handler
110
106
  file = logging.FileHandler('optimuslibLogs.log', 'a', 'utf-8')
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')
107
+ fileformat = logging.Formatter(fmt='%(asctime)s | %(levelname)s | %(filename)s | %(funcName)s | %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
113
108
  file.setLevel(logging.DEBUG)
114
- # file.setLevel(logging.INFO)
115
109
  file.setFormatter(fileformat)
116
110
 
117
- # # Handler - 2
111
+ # Handler - 2: Stream Handler
118
112
  stream = logging.StreamHandler()
119
- # streamformat = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
120
- streamformat = fileformat
121
113
  stream.setLevel(logging.DEBUG)
122
- # stream.setLevel(logging.INFO)
123
- stream.setFormatter(streamformat)
114
+ stream.setFormatter(fileformat)
124
115
 
125
- # # Adding all handlers to the logs
116
+ # Adding handlers to the logger (not to root logger to avoid duplicates)
126
117
  log.addHandler(file)
127
118
  log.addHandler(stream)
128
119
 
120
+ # Prevent propagation to root logger to avoid duplicate logs
121
+ log.propagate = False
122
+
129
123
  def loglib(data):
130
124
  log.info(data)
131
125
  log.info('%d %s','1', 'a')
@@ -1552,21 +1546,43 @@ from datetime import datetime, timezone
1552
1546
 
1553
1547
 
1554
1548
  def extract_clean_text(msg):
1555
- """Extract readable text from HTML email"""
1556
- text = ""
1549
+ text_parts = []
1550
+
1557
1551
  for part in msg.walk():
1558
- if part.get_content_type() == "text/html":
1552
+ content_type = part.get_content_type()
1553
+ charset = part.get_content_charset()
1554
+
1555
+ log.debug(f"Found MIME part: {content_type}, charset={charset}")
1556
+
1557
+ if content_type in ("text/plain", "text/html"):
1559
1558
  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
1559
 
1560
+ if not raw:
1561
+ log.debug("Empty payload")
1562
+ continue
1563
+
1564
+ try:
1565
+ decoded = raw.decode(charset or "utf-8", errors="ignore")
1566
+ except Exception:
1567
+ decoded = quopri.decodestring(raw).decode(errors="ignore")
1568
+
1569
+ if content_type == "text/html":
1570
+ soup = BeautifulSoup(decoded, "html.parser")
1571
+ decoded = soup.get_text(" ")
1565
1572
 
1566
- def is_recent(msg):
1567
- """Check if email was received within MAX_AGE_SECONDS"""
1573
+ text_parts.append(decoded)
1574
+
1575
+ full_text = " ".join(text_parts)
1576
+ log.debug(f"Extracted email text (first 300 chars): {full_text[:300]!r}")
1577
+
1578
+ return full_text
1579
+
1580
+
1581
+
1582
+ def is_recent(msg, max_age_seconds):
1568
1583
  date_hdr = msg.get("Date")
1569
1584
  if not date_hdr:
1585
+ log.debug("Email has no Date header")
1570
1586
  return False
1571
1587
 
1572
1588
  try:
@@ -1576,10 +1592,13 @@ def is_recent(msg):
1576
1592
 
1577
1593
  now = datetime.now(timezone.utc)
1578
1594
  age = (now - msg_time).total_seconds()
1579
- return 0 <= age <= MAX_AGE_SECONDS
1580
- except Exception:
1581
- return False
1582
1595
 
1596
+ log.debug(f"Email age (seconds): {age}")
1597
+ return 0 <= age <= max_age_seconds
1598
+
1599
+ except Exception as e:
1600
+ log.exception("Failed to parse email date")
1601
+ return False
1583
1602
 
1584
1603
  def input_with_timeout(prompt, timeout):
1585
1604
  """Get user input with timeout"""
@@ -1612,28 +1631,46 @@ def poll_for_otp(EMAIL, APP_PASSWORD, SENDER, SUBJECT, POLL_INTERVAL, POLL_TIMEO
1612
1631
  # 🔑 Gmail IMAP requires re-select to refresh UNSEEN
1613
1632
  mail.select("inbox", readonly=False)
1614
1633
 
1615
- status, messages = mail.search(
1616
- None,
1617
- f'(UNSEEN FROM "{SENDER}" SUBJECT "{SUBJECT}")'
1618
- )
1634
+ filter = f'(UNSEEN FROM "{SENDER}" SUBJECT "{SUBJECT}")'
1635
+ log.debug(f"IMAP search filter: {filter}")
1636
+ status, messages = mail.search(None,filter)
1619
1637
 
1620
1638
  ids = messages[0].split()
1621
1639
 
1640
+ log.debug(f"IMAP search status: {status}")
1641
+ log.debug(f"Message IDs returned: {ids}")
1642
+
1622
1643
  for eid in ids:
1623
1644
  _, data = mail.fetch(eid, "(RFC822)")
1624
1645
  msg = email.message_from_bytes(data[0][1])
1625
1646
 
1626
- if not is_recent(msg):
1647
+ log.debug(f"From: {msg.get('From')}")
1648
+ log.debug(f"Subject: {msg.get('Subject')}")
1649
+ log.debug(f"Date: {msg.get('Date')}")
1650
+
1651
+ if not is_recent(msg, MAX_AGE_SECONDS):
1627
1652
  continue
1628
1653
 
1629
1654
  body = extract_clean_text(msg)
1630
- match = re.search(r"OTP[^0-9]*(\d{6})", body, re.IGNORECASE)
1631
1655
 
1632
- if match:
1633
- # mark email as read
1634
- mail.store(eid, "+FLAGS", "\\Seen")
1635
- mail.logout()
1636
- return match.group(1)
1656
+ if not body.strip():
1657
+ log.warning("Email body extracted but empty")
1658
+ continue
1659
+
1660
+ log.debug("Running OTP regex scan")
1661
+
1662
+ match = re.search(r"\b(\d{4,8})\b", body)
1663
+
1664
+ if not match:
1665
+ log.warning("No OTP match found in email body")
1666
+ continue
1667
+
1668
+ otp = match.group(1)
1669
+ log.info(f"OTP extracted successfully: {otp}")
1670
+
1671
+ mail.store(eid, "+FLAGS", "\\Seen")
1672
+ mail.logout()
1673
+ return otp
1637
1674
 
1638
1675
  log.info("Waiting for OTP...")
1639
1676
  time.sleep(POLL_INTERVAL)
@@ -1643,10 +1680,11 @@ def poll_for_otp(EMAIL, APP_PASSWORD, SENDER, SUBJECT, POLL_INTERVAL, POLL_TIMEO
1643
1680
 
1644
1681
 
1645
1682
  def get_gmail_otp(EMAIL, APP_PASSWORD, SENDER, SUBJECT,POLL_INTERVAL,POLL_TIMEOUT,MAX_AGE_SECONDS,MANUAL_INPUT_TIMEOUT):
1683
+
1646
1684
  """Get OTP automatically or fall back to manual input"""
1647
1685
  try:
1648
1686
  otp = poll_for_otp(EMAIL, APP_PASSWORD, SENDER, SUBJECT, POLL_INTERVAL, POLL_TIMEOUT, MAX_AGE_SECONDS)
1649
- log.info("OTP received automatically:", otp)
1687
+ log.info(f"OTP received automatically: {otp}")
1650
1688
  return otp
1651
1689
 
1652
1690
  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.49
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=nZPdZXFPB0uOWqp3xMx5e9d5Yrd3qdRkFWOm7WqCufo,65665
3
+ optimuslib-0.0.49.dist-info/METADATA,sha256=1T3JxGmn2y7HlZVlvhP4IWzOb9g9hZJEbRRWW0iSMd4,723
4
+ optimuslib-0.0.49.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
5
+ optimuslib-0.0.49.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,,