From 9802848c5f3065743102d0ee6f1e68cf1dd6be09 Mon Sep 17 00:00:00 2001 From: Lei PAN Date: Sat, 28 Mar 2026 23:25:07 +0100 Subject: [PATCH] try to use proxy for gmx mail --- src/mail/get_invoices.py | 140 ------- src/mail/imap_proxy_reader.py | 534 +++++++++++++++++++++++++++ src/mail/mail_address_validator.py | 4 +- src/mail/mail_confirmation.py | 32 +- src/mail/mail_constants.py | 17 + src/mail/mail_reader.py | 40 +- src/mail/mail_reader_all_contacts.py | 61 +-- src/passport/card_MRZutile.py | 14 +- src/utils/excel_sanitizer.py | 10 + 9 files changed, 647 insertions(+), 205 deletions(-) delete mode 100644 src/mail/get_invoices.py create mode 100644 src/mail/imap_proxy_reader.py create mode 100644 src/utils/excel_sanitizer.py diff --git a/src/mail/get_invoices.py b/src/mail/get_invoices.py deleted file mode 100644 index a0a9a93..0000000 --- a/src/mail/get_invoices.py +++ /dev/null @@ -1,140 +0,0 @@ -import datetime -import email -import imaplib -import uuid -from concurrent.futures import ThreadPoolExecutor -from email.header import decode_header -from email.message import Message - -from builtins import list -from src.db.mongo_manager import MONGO_STORE_MANAGER -from src.logs.AppLogging import init_logger -from src.mail.mail_constants import DOMAIN_163, DOMAIN_YAHOO, DOMAIN_SINA, IMAP_SERVER_163, YAHOO_IMAP_SERVER, \ - IMAP_SERVER_SINA, AOL_IMAP_SERVER, create_imap -from src.pojo.accepted_appointment_pojo import AcceptedAppointmentPojo -from src.pojo.mail.mail_pojo import MailPojo - -INVOICE_SUBJECT_fr = 'Votre facture' -INVOICE_SUBJECT_EN = 'Your invoice' -VALIDATION_URL_REGEX = """https:\/\/rendezvousparis.hermes.com\/client\/register\/[A-Z0-9]+\/validate.code=[A-Z0-9]+""" -HERMES_INVOICE_EMAIL = "no-reply@hermes.com" - -date_format = "%d-%b-%Y" # DD-Mon-YYYY e.g., 3-Mar-2014 - - -class InvoiceGetter(): - def __init__(self, login, password): - self.login = login - self.password = password - - @staticmethod - def show_folders(imap): - for i in imap.list()[1]: - l = i.decode().split(' "/" ') - print(l[0] + " = " + l[1]) - - - def read_emails(self, mails_messages: list) -> list: - imap = create_imap(self.login) - - # authenticate - type, dat = imap.login(self.login, self.password) - mail_list = [] - print("read mails from {}".format(self.login)) - # self.show_folders(imap) - # total number of emails - # get mails from inbox - # (\Archive \HasNoChildren) = "Archive" - # (\Junk \HasNoChildren) = "Bulk" - # (\Drafts \HasNoChildren) = "Draft" - # (\HasNoChildren) = "Inbox" - # (\Sent \HasNoChildren) = "Sent" - # (\Trash \HasNoChildren) = "Trash" - mail_list.extend(self._get_messages_from_folder(imap, subject=INVOICE_SUBJECT_fr)) - mail_list.extend(self._get_messages_from_folder(imap, subject=INVOICE_SUBJECT_EN)) - # mail_list.extend(self._get_messages_from_folder(imap, folder="Bulk")) - # close the connection and logout - imap.close() - imap.logout() - mails_messages.extend(mail_list) - return mail_list - - def _get_messages_from_folder(self, imap, subject, folder="INBOX") -> list: - imap.select(folder) - mail_messages = [] - typ, data = imap.search(None, '(SUBJECT "{}" SINCE "{}")'.format(subject, datetime.datetime.today().strftime( - date_format))) - for i in data[0].split(): - # fetch the email message by ID - res, msg = imap.fetch(i.decode("utf-8"), "(RFC822)") - body = '' - for response in msg: - if isinstance(response, tuple): - # parse a bytes email into a message object - msg = email.message_from_bytes(response[1]) - # decode the email subject - subject, subject_encoded = decode_header(msg["Subject"])[0] - received_date = msg["Date"] - if isinstance(subject, bytes): - # if it's a bytes, decode to str - subject = subject.decode(subject_encoded) - # decode email sender - from_address, subject_encoded = decode_header(msg.get("From"))[0] - if isinstance(from_address, bytes): - from_address = from_address.decode(subject_encoded) - print("From:", from_address) - print("Subject:", subject) - # if the email message is multipart - if msg.is_multipart(): - # iterate over email parts - for part in msg.walk(): - try: - # get the email body - payloads = part.get_payload() - if isinstance(payloads, list): - for payload in payloads: - if isinstance(payload, Message): - if payload.get_content_type() == 'text/html': - body = body + payload.get_payload(decode=True).decode("iso-8859-1") - elif payload.get_content_type() == 'application/pdf': - # save to pdf - open("./" + self.login + str(uuid.uuid4()) + '.pdf', 'wb').write( - payload.get_payload(decode=True)) - - # print(body) - except Exception as Error: - print(Error) - else: - body = msg.get_payload(decode=True).decode() - print(body) - if INVOICE_SUBJECT_fr in subject or INVOICE_SUBJECT_EN in subject: - mail = MailPojo(subject=subject, body=body, from_address=from_address) - mail_messages.append(mail) - return mail_messages - - -def get_invoices(): - # get email address - mail_list = MONGO_STORE_MANAGER.get_destination_emails() - mail_list_to_check = [] - # yesterday = str(datetime.date.today() - datetime.timedelta(days=2)) - yesterday = str(datetime.date.today()) - collection = MONGO_STORE_MANAGER.get_accepted_items_for_one_day(yesterday) - for valid_appointment in collection: - accepted_pojo = AcceptedAppointmentPojo.from_reserve(valid_appointment) - for mail in mail_list: - if mail.mail == accepted_pojo.email: - mail_list_to_check.append(mail) - # mail_list = [mail_address1] - mails_messages = [] - with ThreadPoolExecutor(max_workers=20) as executor: - for mail in mail_list_to_check: - # check whether we need to read mail - mail_reader = InvoiceGetter(mail.mail, mail.password) - executor.submit(mail_reader.read_emails, mails_messages) - - -# check whether the url has already been clicked -if __name__ == '__main__': - init_logger() - get_invoices() diff --git a/src/mail/imap_proxy_reader.py b/src/mail/imap_proxy_reader.py new file mode 100644 index 0000000..c1a94da --- /dev/null +++ b/src/mail/imap_proxy_reader.py @@ -0,0 +1,534 @@ +""" +imap_proxy_reader.py +==================== +Lire des emails via IMAPClient en passant par un proxy SOCKS5/SOCKS4/HTTP. + +Fonctionnement : + - ProxyIMAP4_TLS : sous-classe de imaplib.IMAP4 qui ouvre la socket + à travers un proxy SOCKS via PySocks. + - ProxyIMAPClient : sous-classe de IMAPClient qui injecte ProxyIMAP4_TLS + au lieu de la connexion directe habituelle. + +Dépendances : + pip install imapclient PySocks +""" + +import datetime +import email +import imaplib +import io +import logging +import os +import re +import ssl +import socket +from dataclasses import dataclass, field +from email.message import Message +from typing import List, Optional, Tuple + +import socks +from dotenv import load_dotenv +from imapclient import IMAPClient + +load_dotenv() + +# ────────────────────────────────────────────────────────────── +# Constantes +# ────────────────────────────────────────────────────────────── + +VALIDATION_URL_SUBJECT_FR = "Validation de votre demande de rendez-vous" +VALIDATION_URL_SUBJECT_EN = "Please confirm your appointment request" +VALIDATION_URL_REGEX = ( + r"https:\/\/rendezvousparis\.hermes\.com" + r"\/client\/register\/[A-Z0-9]+\/validate\.code=[A-Z0-9]+" +) + +DATE_FORMAT = "%d-%b-%Y" + +# Correspondance domaine → serveur IMAP (identique à mail_constants.py) +IMAP_SERVER_MAP: List[Tuple[str, str]] = [ + ("163.com", "imap.163.com"), + ("yahoo.com", "imap.mail.yahoo.com"), + ("firemail.de", "imap.firemail.de"), + ("gmail.com", "imap.gmail.com"), + ("sina.com", "imap.sina.com"), + ("hotmail.com", "outlook.office365.com"), + ("outlook.com", "outlook.office365.com"), + ("rambler.ru", "imap.rambler.ru"), + ("btvm.ne.jp", "imap.btvm.ne.jp"), + ("mars.dti.ne.jp", "imap.cm.dream.jp"), + ("aurora.dti.ne.jp", "imap.cm.dream.jp"), + ("naver.com", "imap.naver.com"), + ("onet.pl", "imap.poczta.onet.pl"), + ("gazeta.pl", "imap.gazeta.pl"), + ("tim.it", "imap.tim.it"), + ("alice.it", "in.alice.it"), + ("gmx.com", "imap.gmx.com"), + ("gmx.fr", "imap.gmx.com"), + ("gmx.us", "imap.gmx.com"), + ("gmx.ch", "imap.gmx.com"), + ("gmx.pt", "imap.gmx.com"), + ("gmx.sg", "imap.gmx.com"), + ("gmx.net", "imap.gmx.net"), + ("gmx.de", "imap.gmx.net"), + ("gmx.at", "imap.gmx.at"), + ("web.de", "imap.web.de"), + ("inbox.lv", "mail.inbox.lv"), + ("pissmail.com", "mail.pissmail.com"), + ("incel.email", "mail.pissmail.com"), + ("shitposting.expert","mail.pissmail.com"), + ("hatesje.ws", "mail.pissmail.com"), + ("child.pizza", "mail.pissmail.com"), + ("genocide.fun", "mail.pissmail.com"), + ("dmc.chat", "mail.pissmail.com"), + ("aol.com", "imap.aol.com"), # fallback AOL +] + +PROXY_TYPE_MAP = { + "SOCKS5": socks.SOCKS5, + "SOCKS4": socks.SOCKS4, + "HTTP": socks.HTTP, +} + +logger = logging.getLogger(__name__) + + +# ────────────────────────────────────────────────────────────── +# Modèles de données +# ────────────────────────────────────────────────────────────── + +@dataclass +class ProxyConfig: + """Configuration du proxy.""" + host: str + port: int + proxy_type: str = "SOCKS5" # "SOCKS5" | "SOCKS4" | "HTTP" + username: Optional[str] = None + password: Optional[str] = None + + @property + def socks_type(self) -> int: + t = self.proxy_type.upper() + if t not in PROXY_TYPE_MAP: + raise ValueError(f"proxy_type invalide : {self.proxy_type!r}. " + f"Valeurs autorisées : {list(PROXY_TYPE_MAP)}") + return PROXY_TYPE_MAP[t] + + def __repr__(self) -> str: + auth = f"{self.username}:***@" if self.username else "" + return f"{self.proxy_type}://{auth}{self.host}:{self.port}" + + +@dataclass +class MailAccount: + """Compte email à lire.""" + login: str + password: str + + +@dataclass +class MailResult: + """Résultat d'une lecture d'email.""" + account: str + subject: str + from_address: str + to_address: str + body: str + validation_urls: List[str] = field(default_factory=list) + + +# ────────────────────────────────────────────────────────────── +# Connexion IMAP via proxy (bas niveau) +# ────────────────────────────────────────────────────────────── + +class ProxyIMAP4_TLS(imaplib.IMAP4): + """ + Variante TLS de imaplib.IMAP4 qui route la connexion + à travers un proxy SOCKS5/SOCKS4/HTTP grâce à PySocks. + """ + + def __init__( + self, + host: str, + port: int, + ssl_context: Optional[ssl.SSLContext], + proxy: ProxyConfig, + timeout: Optional[float] = None, + ): + self._ssl_context = ssl_context + self._proxy = proxy + self._timeout = timeout + # imaplib.IMAP4.__init__ appelle self.open() + imaplib.IMAP4.__init__(self, host, port) + self.file: io.BufferedReader + + def open(self, host: str = "", port: int = 993, timeout: Optional[float] = None) -> None: + self.host = host + self.port = port + effective_timeout = timeout if timeout is not None else self._timeout + + # ── Créer la socket SOCKS ──────────────────────────── + sock = socks.socksocket(socket.AF_INET, socket.SOCK_STREAM) + sock.set_proxy( + proxy_type=self._proxy.socks_type, + addr=self._proxy.host, + port=self._proxy.port, + username=self._proxy.username, + password=self._proxy.password, + ) + if effective_timeout: + sock.settimeout(effective_timeout) + sock.connect((host, port)) + + # ── Envelopper avec SSL/TLS ────────────────────────── + ctx = self._ssl_context or ssl.create_default_context() + self.sock = ctx.wrap_socket(sock, server_hostname=host) + self.file = self.sock.makefile("rb") + + # ── Méthodes requises par imaplib.IMAP4 ───────────────── + def read(self, size: int) -> bytes: + return self.file.read(size) # type: ignore[return-value] + + def readline(self) -> bytes: + return self.file.readline() # type: ignore[return-value] + + def send(self, data) -> None: + self.sock.sendall(data) + + def shutdown(self) -> None: + imaplib.IMAP4.shutdown(self) + + +# ────────────────────────────────────────────────────────────── +# IMAPClient avec proxy +# ────────────────────────────────────────────────────────────── + +class ProxyIMAPClient(IMAPClient): + """ + Sous-classe d'IMAPClient qui utilise un proxy SOCKS/HTTP. + + Usage : + proxy = ProxyConfig(host="127.0.0.1", port=1080, proxy_type="SOCKS5") + client = ProxyIMAPClient("imap.gmail.com", proxy=proxy, use_uid=True) + client.login("user@gmail.com", "password") + """ + + def __init__(self, host: str, proxy: ProxyConfig, **kwargs): + self._proxy = proxy + super().__init__(host, **kwargs) + + def _create_IMAP4(self): + """Remplace la méthode d'IMAPClient pour injecter ProxyIMAP4_TLS.""" + if self.ssl: + return ProxyIMAP4_TLS( + host=self.host, + port=self.port, + ssl_context=self.ssl_context, + proxy=self._proxy, + timeout=getattr(self._timeout, "connect", None), + ) + # Connexion non-SSL à travers le proxy (rare, mais supporté) + # On monkey-patch juste la connexion TCP + raise NotImplementedError( + "Connexion IMAP non-SSL via proxy non implémentée. " + "Utilisez ssl=True (port 993)." + ) + + +# ────────────────────────────────────────────────────────────── +# Fonctions utilitaires +# ────────────────────────────────────────────────────────────── + +def get_imap_server(login: str) -> str: + """Retourne le serveur IMAP correspondant au domaine du login.""" + login_lower = login.lower() + for domain, server in IMAP_SERVER_MAP: + if domain in login_lower: + return server + return "imap.aol.com" # fallback + + +def extract_body(email_message: Message) -> str: + """Extrait le corps HTML ou texte d'un email.""" + body = "" + for part in email_message.walk(): + content_type = part.get_content_type() + try: + if content_type == "text/html": + payload = part.get_payload(decode=True) + if payload: + body += payload.decode("utf-8", errors="ignore") + elif content_type == "text/plain": + payload = part.get_payload() + if payload: + body += str(payload) + except Exception as exc: + logger.warning("Erreur extraction body : %s", exc) + return body + + +def find_validation_urls(text: str) -> List[str]: + """Recherche toutes les URLs de validation Hermes dans un texte.""" + return re.findall(VALIDATION_URL_REGEX, text) + + +# ────────────────────────────────────────────────────────────── +# Lecteur principal +# ────────────────────────────────────────────────────────────── + +class ProxyMailReader: + """ + Lit les emails d'un compte via IMAPClient en passant par un proxy. + + Paramètres + ---------- + account : MailAccount + Identifiants du compte email. + proxy : ProxyConfig + Configuration du proxy. + timeout : float, optional + Timeout de connexion en secondes (défaut : 30 s). + """ + + def __init__( + self, + account: MailAccount, + proxy: ProxyConfig, + timeout: float = 30.0, + ): + self.account = account + self.proxy = proxy + self.timeout = timeout + + # ── Connexion ──────────────────────────────────────────── + + def _connect(self) -> ProxyIMAPClient: + imap_server = get_imap_server(self.account.login) + logger.info( + "[%s] Connexion via %s → %s:993", + self.account.login, self.proxy, imap_server, + ) + client = ProxyIMAPClient( + host=imap_server, + proxy=self.proxy, + use_uid=True, + ssl=True, + timeout=self.timeout, + ) + client.login(self.account.login, self.account.password) + logger.info("[%s] Connecté.", self.account.login) + return client + + # ── Lecture des dossiers ───────────────────────────────── + + def _list_folders(self, client: ProxyIMAPClient) -> List[str]: + return [info[-1] for info in client.list_folders()] + + # ── Lecture des messages ───────────────────────────────── + + def _read_folder( + self, + client: ProxyIMAPClient, + folder: str, + since: Optional[datetime.datetime] = None, + ) -> List[MailResult]: + results: List[MailResult] = [] + since = since or datetime.datetime.today() + + try: + client.select_folder(folder, readonly=True) + except Exception as exc: + logger.warning("[%s] Impossible d'ouvrir '%s' : %s", + self.account.login, folder, exc) + return results + + try: + uids = client.search(["SINCE", since]) + except Exception as exc: + logger.warning("[%s] Recherche échouée dans '%s' : %s", + self.account.login, folder, exc) + return results + + if not uids: + return results + + logger.info("[%s] %d message(s) dans '%s'", + self.account.login, len(uids), folder) + + for uid, msg_data in client.fetch(uids, "RFC822").items(): + try: + raw = msg_data.get(b"RFC822") or msg_data.get("RFC822") + if raw is None: + continue + em = email.message_from_bytes(raw) + + subject = em.get("Subject", "") + from_addr = em.get("From", "") + to_addr = em.get("To", self.account.login) + + # Filtrer : on ne garde que les emails de validation Hermes + is_validation = ( + VALIDATION_URL_SUBJECT_FR in subject + or VALIDATION_URL_SUBJECT_EN in subject + or "no-reply@hermes.com" in from_addr.lower() + ) + if not is_validation: + continue + + body = extract_body(em) + urls = find_validation_urls(body) + + result = MailResult( + account=self.account.login, + subject=subject, + from_address=from_addr, + to_address=to_addr, + body=body, + validation_urls=urls, + ) + results.append(result) + logger.info( + "[%s] Email de validation trouvé (uid=%s) — URLs : %s", + self.account.login, uid, urls or "aucune", + ) + except Exception as exc: + logger.warning( + "[%s] Erreur traitement uid=%s : %s", + self.account.login, uid, exc, + ) + + return results + + # ── Point d'entrée public ──────────────────────────────── + + def read( + self, + since: Optional[datetime.datetime] = None, + skip_folders: Optional[List[str]] = None, + ) -> List[MailResult]: + """ + Se connecte au serveur IMAP via le proxy et retourne la liste + des emails de validation trouvés depuis `since` (aujourd'hui par défaut). + + Paramètres + ---------- + since : datetime, optional — date de début de recherche + skip_folders : list[str], optional — dossiers à ignorer + (défaut : ["Sent", "Drafts", "Trash", "Junk", "Spam"]) + """ + if skip_folders is None: + skip_folders = ["Sent", "Drafts", "Trash", "Junk", "Spam"] + + all_results: List[MailResult] = [] + client = self._connect() + + try: + folders = self._list_folders(client) + logger.info("[%s] Dossiers : %s", self.account.login, folders) + + for folder in folders: + if folder in skip_folders: + logger.debug("[%s] Dossier ignoré : %s", + self.account.login, folder) + continue + all_results.extend(self._read_folder(client, folder, since)) + finally: + try: + client.logout() + except Exception: + pass + + return all_results + + +# ────────────────────────────────────────────────────────────── +# Lecture parallèle de plusieurs comptes +# ────────────────────────────────────────────────────────────── + +from concurrent.futures import ThreadPoolExecutor, as_completed + + +def read_multiple_accounts( + accounts: List[MailAccount], + proxy: ProxyConfig, + since: Optional[datetime.datetime] = None, + max_workers: int = 10, + timeout: float = 30.0, +) -> List[MailResult]: + """ + Lit plusieurs comptes email en parallèle via le même proxy. + + Retourne la liste consolidée de tous les MailResult trouvés. + """ + all_results: List[MailResult] = [] + + with ThreadPoolExecutor(max_workers=max_workers) as executor: + future_map = { + executor.submit( + ProxyMailReader(acc, proxy, timeout).read, since + ): acc.login + for acc in accounts + } + for future in as_completed(future_map): + login = future_map[future] + try: + results = future.result() + logger.info("[%s] %d email(s) de validation récupéré(s).", + login, len(results)) + all_results.extend(results) + except Exception as exc: + logger.error("[%s] Erreur : %s", login, exc) + + return all_results + + +# ────────────────────────────────────────────────────────────── +# Point d'entrée — exemple d'utilisation +# ────────────────────────────────────────────────────────────── + +if __name__ == "__main__": + logging.basicConfig( + level=logging.INFO, + format="%(asctime)s %(levelname)-8s %(message)s", + datefmt="%H:%M:%S", + ) + + # ── 1. Configurer le proxy ─────────────────────────────── + proxy = ProxyConfig( + host=os.environ.get("GMX_PROXY_HOST", ""), + port=int(os.environ.get("GMX_PROXY_PORT", "443")), + proxy_type=os.environ.get("GMX_PROXY_TYPE", "SOCKS5"), + username=os.environ.get("GMX_PROXY_USERNAME"), + password=os.environ.get("GMX_PROXY_PASSWORD"), + ) + + # ── 2. Définir les comptes à lire ──────────────────────── + accounts = [ + MailAccount(login="birgitnaya@gmx.net", password="XEeUF3Y1yaO"), + # MailAccount(login="user@gmail.com", password="apppassword"), + # MailAccount(login="user@outlook.com", password="password"), + ] + + # ── 3. Lancer la lecture ───────────────────────────────── + results = read_multiple_accounts( + accounts=accounts, + proxy=proxy, + since=datetime.datetime.today(), + max_workers=5, + timeout=30.0, + ) + + # ── 4. Afficher les résultats ──────────────────────────── + print(f"\n{'='*60}") + print(f" {len(results)} email(s) de validation trouvé(s)") + print(f"{'='*60}\n") + + for r in results: + print(f" Compte : {r.account}") + print(f" De : {r.from_address}") + print(f" Sujet : {r.subject}") + print(f" URLs : {r.validation_urls or 'aucune'}") + print(f" {'-'*56}") + + + diff --git a/src/mail/mail_address_validator.py b/src/mail/mail_address_validator.py index a491237..aa06e63 100755 --- a/src/mail/mail_address_validator.py +++ b/src/mail/mail_address_validator.py @@ -126,5 +126,5 @@ def check_mails(): if __name__ == '__main__': - # remove_invalid_email() - check_mails() \ No newline at end of file + remove_invalid_email() + # check_mails() \ No newline at end of file diff --git a/src/mail/mail_confirmation.py b/src/mail/mail_confirmation.py index 987377a..9dc2ff8 100755 --- a/src/mail/mail_confirmation.py +++ b/src/mail/mail_confirmation.py @@ -11,7 +11,9 @@ from imapclient import IMAPClient from src.db.mirgration.migration_tools import migre_accepted_appointment from src.db.mongo_manager import MONGO_STORE_MANAGER -from src.mail.mail_constants import create_imap, show_folders +from src.mail.mail_constants import create_imap, show_folders, is_gmx_address +from src.mail.mail_reader import get_gmx_proxy_config +from src.mail.imap_proxy_reader import ProxyMailReader, MailAccount, ProxyConfig from src.notification.AcceptedResultPojo import get_accepted_result_from from src.notification.mailer import Mailer from src.pojo.ResultEnum import ResultEnum @@ -26,6 +28,17 @@ date_format = "%d-%b-%Y" # DD-Mon-YYYY e.g., 3-Mar-2014 FRENCH_CONFIRMED_MESSAGE = "Nous aurons le plaisir de vous accueillir" +def read_gmx_proxy_confirmation_emails(mail, mails_messages: list, proxy_config: ProxyConfig) -> None: + account = MailAccount(login=mail.mail, password=mail.password) + results = ProxyMailReader(account, proxy_config).read(since=datetime.datetime.today()) + for result in results: + mail_pojo = MailPojo(subject=result.subject, body=result.body, from_address=result.from_address) + mail_pojo.mail_address = mail.mail + mail_pojo.to_address = result.to_address or mail.mail + mail_pojo.isImapClient = True + mails_messages.append(mail_pojo) + + class MailConfirmationReader(): def __init__(self, login, password): self.login = login @@ -154,7 +167,7 @@ def accept_appointment_found(accepted_result_list: list): for user in _all_register_account: if user.mail == result.email: result.account_password = user.password - mailer.send_email(result, to_all=True) + mailer.send_email(result, to_all=False) MONGO_STORE_MANAGER.update_reserve_result(reserve.id, ResultEnum.ACCEPTED, reserve.message) # sginal.send_result(result) @@ -181,15 +194,17 @@ def find_confirmation_contacts_for_today(): def find_confirmation_contacts_mail_list(mail_list): - # mail_list = [MailAddress("saigecong1990@pissmail.com", "cvExXKOP8oY1D@")] mail_list.append(MailAddress("saigecong1990@pissmail.com", "cvExXKOP8oY1D@")) mails_messages = [] + gmx_proxy_config = get_gmx_proxy_config() # read all the emails with ThreadPoolExecutor(max_workers=200) as executor: for mail in mail_list: - # if DOMAIN_HOTMAIL not in mail.mail: - mail_reader = MailConfirmationReader(mail.mail, mail.password) - executor.submit(mail_reader.read_emails, mails_messages) + if is_gmx_address(mail.mail) and gmx_proxy_config is not None: + executor.submit(read_gmx_proxy_confirmation_emails, mail, mails_messages, gmx_proxy_config) + else: + mail_reader = MailConfirmationReader(mail.mail, mail.password) + executor.submit(mail_reader.read_emails, mails_messages) accepted_appointment_list = [] if len(mails_messages) > 0: successful_items = MONGO_STORE_MANAGER.get_all_successful_items_for_day() @@ -229,11 +244,6 @@ def read_mails_and_find_confirmation_contacts(all_mails=False): else: mail_list = find_confirmation_contacts_for_today() return find_confirmation_contacts_mail_list(mail_list) - # mail_list.reverse() - # excel_reader = ExcelHelper() - # mail_list =excel_reader.read_email_pojo(file_name="/Users/lpan/Desktop/hotmail_list.xlsx") - # mail_address3 = MailAddress(mail="taibenchragu1978@onet.pl", password="2J)kyfNgyOZ") - # mail_list = [mail_address3] # init_logger() diff --git a/src/mail/mail_constants.py b/src/mail/mail_constants.py index bc5441a..f2342d4 100755 --- a/src/mail/mail_constants.py +++ b/src/mail/mail_constants.py @@ -27,6 +27,23 @@ DOMAIN_NAVER = "naver.com" DOMAIN_INBOX_LV = "inbox.lv" DOMAIN_GMX_DE = "gmx.de" +GMX_DOMAINS = { + DOMAIN_GMX, + DOMAIN_GMX_NET, + DOMAIN_GMX_AT, + DOMAIN_GMX_FR, + DOMAIN_GMX_US, + DOMAIN_GMX_SG, + DOMAIN_GMX_CH, + DOMAIN_GMX_PT, + DOMAIN_GMX_DE, +} + + +def is_gmx_address(login: str) -> bool: + return any(domain in login for domain in GMX_DOMAINS) + + DOMAIN_PISS_MAIL = "pissmail.com" DOMAIN_INCEL_EMAIL = "incel.email" DOMAIN_SHITPOSTING_EXPERT = "shitposting.expert" diff --git a/src/mail/mail_reader.py b/src/mail/mail_reader.py index 399ceec..eac63b8 100755 --- a/src/mail/mail_reader.py +++ b/src/mail/mail_reader.py @@ -1,6 +1,8 @@ import datetime import email import re +import os +from typing import Optional from builtins import list from concurrent.futures import ThreadPoolExecutor from datetime import time @@ -11,7 +13,8 @@ from imapclient import IMAPClient from src.db.mongo_manager import MONGO_STORE_MANAGER from src.logs.AppLogging import init_logger -from src.mail.mail_constants import create_imap, show_folders +from src.mail.mail_constants import create_imap, show_folders, is_gmx_address +from src.mail.imap_proxy_reader import ProxyMailReader, ProxyConfig, MailAccount from src.pojo.mail.mail_pojo import MailPojo from src.utils.timeutiles import is_time_between @@ -51,6 +54,33 @@ def find_from_mail(param): return from_address.strip(" ").strip(">").strip("<") +def get_gmx_proxy_config() -> Optional[ProxyConfig]: + host = os.environ.get("GMX_PROXY_HOST", "") + if not host: + return None + try: + port = int(os.environ.get("GMX_PROXY_PORT", "443")) + except ValueError: + port = 443 + return ProxyConfig( + host=host, + port=port, + proxy_type=os.environ.get("GMX_PROXY_TYPE", "SOCKS5"), + username=os.environ.get("GMX_PROXY_USERNAME"), + password=os.environ.get("GMX_PROXY_PASSWORD"), + ) + + +def read_gmx_proxy_emails(mail, mails_messages: list, proxy_config: ProxyConfig) -> None: + account = MailAccount(login=mail.mail, password=mail.password) + results = ProxyMailReader(account, proxy_config).read(since=datetime.datetime.today()) + for result in results: + mail_pojo = MailPojo(subject=result.subject, body=result.body, from_address=result.from_address) + mail_pojo.mail_address = mail.mail + mail_pojo.to_address = result.to_address or mail.mail + mails_messages.append(mail_pojo) + + class MailReader(): def __init__(self, login, password): self.login = login @@ -241,6 +271,7 @@ def read_mails(): if is_time_between(time(7, 30), time(23, 30)): # get email address mail_list = MONGO_STORE_MANAGER.get_destination_emails() + gmx_proxy_config = get_gmx_proxy_config() # excel_reader = ExcelHelper() # mail_list = excel_reader.read_email_pojo(file_name="/Users/panlei/Downloads/hotmail_list.xlsx") # mail_address1 = MailAddress(mail="casandrakaamv@onet.pl", password="8F0o0APeAp0z") @@ -251,8 +282,11 @@ def read_mails(): for mail in mail_list: # check whether we need to read mail if need_to_check_email(mail.mail, successful_items): - mail_reader = MailReader(mail.mail, mail.password) - executor.submit(mail_reader.read_emails, mails_messages) + if is_gmx_address(mail.mail) and gmx_proxy_config is not None: + executor.submit(read_gmx_proxy_emails, mail, mails_messages, gmx_proxy_config) + else: + mail_reader = MailReader(mail.mail, mail.password) + executor.submit(mail_reader.read_emails, mails_messages) # get ip_country info _refreshed_successful_items = MONGO_STORE_MANAGER.get_all_successful_items_for_day() _all_contact_list = MONGO_STORE_MANAGER.get_all_contacts_to_book() diff --git a/src/mail/mail_reader_all_contacts.py b/src/mail/mail_reader_all_contacts.py index d1f4021..cf61390 100755 --- a/src/mail/mail_reader_all_contacts.py +++ b/src/mail/mail_reader_all_contacts.py @@ -11,8 +11,8 @@ from imapclient import IMAPClient from src.db.mongo_manager import MONGO_STORE_MANAGER from src.logs.AppLogging import init_logger -from src.mail.mail_constants import DOMAIN_HOTMAIL, create_imap -from src.mail.mail_reader import need_to_valid_url +from src.mail.mail_constants import DOMAIN_HOTMAIL, create_imap, is_gmx_address +from src.mail.mail_reader import need_to_valid_url, get_gmx_proxy_config, read_gmx_proxy_emails from src.pojo.mail.mail_pojo import MailPojo from src.utils.excel_reader import read_contacts from src.utils.timeutiles import is_time_between @@ -262,6 +262,7 @@ def read_all_mails(contact_to_book_list=None): if contact_to_book_list is None: contact_to_book_list = MONGO_STORE_MANAGER.get_all_contact_to_book_list() mail_list = MONGO_STORE_MANAGER.get_destination_emails() + gmx_proxy_config = get_gmx_proxy_config() mail_list_to_check = [] for contact in contact_to_book_list: for mail in mail_list: @@ -273,8 +274,11 @@ def read_all_mails(contact_to_book_list=None): for mail in mail_list_to_check: # check whether we need to read mail if need_to_check_email(mail.mail, successful_items): - mail_reader = MailReader(mail.mail, mail.password) - executor.submit(mail_reader.read_emails, mails_messages) + if is_gmx_address(mail.mail) and gmx_proxy_config is not None: + executor.submit(read_gmx_proxy_emails, mail, mails_messages, gmx_proxy_config) + else: + mail_reader = MailReader(mail.mail, mail.password) + executor.submit(mail_reader.read_emails, mails_messages) # 在读邮件时候,可能会有其他的约会提交或者约会的链接确认,所以需要刷新一下成功的列表 _refreshed_successful_items = MONGO_STORE_MANAGER.get_all_successful_items_for_day() contact_serial_map_list = MONGO_STORE_MANAGER.get_all_contact_serial_list() @@ -300,56 +304,29 @@ if __name__ == '__main__': contact_to_book_list = read_contacts( # file_name="/Users/lpan/Desktop/contact_list_2025-01-16_yahoo_100.xlsx") # file_name="/Users/lpan/Desktop/contact_list_2025-03-15.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-24.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-15.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-21.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-20.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-14.xlsx") - # file_name="/Users/lpan/Desktop/contact_aol_200_2025-01-15.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-18.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-17.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-19.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-15.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-01-25.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-13.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-12.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-11.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-04.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-10.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-09.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-06.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-07.xlsx") # file_name="/Users/lpan/Desktop/extracted_yahoo_contacts_129_24_03_win.xlsx") # file_name="/Users/lpan/Desktop/contact_list_2025-04-02.xlsx") # file_name="/Users/lpan/Desktop/extracted_aol_contacts_292_24_03_mac.xlsx") # file_name="/Users/lpan/Desktop/real_name_contacts_197_24_03.xlsx")x # file_name="/Users/lpan/Desktop/contact_list_2025-03-29.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-31.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_all_21.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_all_10.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_all_14.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_all_19.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_all_22.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_all_2_win.xlsx") # file_name="/Users/lpan/Desktop/contact_list_2025-03-29.xlsx") # file_name="/Users/lpan/Desktop/17_18_04_to_test_win.xlsx") # file_name="/Users/lpan/Desktop/real_name_contacts_200_aol_win.xlsx") # file_name="/Users/lpan/Desktop/real_name_contacts_200_aol_mac.xlsx") # file_name="/Users/lpan/Desktop/real_name_contacts_500_27_03_25_fixed_mac.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-28_mac.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-27_mac.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-27_win.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_all_1.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_all_12.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-04-05.xlsx") - # file_name="/Users/lpan/Desktop/real_name_contacts_400_aol_mac.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-04-07.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-04-05.xlsx") - # file_name="/Users/lpan/Desktop/real_name_contacts_100_gmx_ch_05_04_mac.xlsx") - file_name="/Users/lpan/Desktop/real_name_contacts_292_win.xlsx") + # file_name="/Users/panlei/Desktop/contact_list_2025-04-08.xlsx") + # file_name="/Users/panlei/Desktop/contact_list_2025-04-10.xlsx") + # file_name="/Users/panlei/Desktop/contact_list_2024-10-02.xlsx") + file_name="/Users/panlei/Desktop/contact_list_2025-08-18_no_ms.xlsx") + # file_name="/Users/panlei/Desktop/real_name_contacts_200_gmx_ch_8_04_mac.xlsx") + # file_name="/Users/lpan/Desktop/contact_list_2025-04-05.xlsx") + # file_name="/Users/lpan/Desktop/real_name_contacts_400_aol_mac.xlsx") + # file_name="/Users/lpan/Desktop/contact_list_2025-04-07.xlsx") + # file_name="/Users/lpan/Desktop/contact_list_2025-04-05.xlsx") + # file_name="/Users/lpan/Desktop/real_name_contacts_100_gmx_ch_05_04_mac.xlsx") + # file_name="/Users/lpan/Desktop/real_name_contacts_292_win.xlsx") # file_name="/Users/lpan/Desktop/real_name_contacts_400_mac_04_04.xlsx") # file_name="/Users/lpan/Desktop/contact_list_2025-04-01.xlsx") - # file_name="/Users/lpan/Desktop/contact_list_2025-03-08_aol_400.xlsx") read_all_mails(contact_to_book_list=contact_to_book_list) # read_all_mails() diff --git a/src/passport/card_MRZutile.py b/src/passport/card_MRZutile.py index 2f43da3..ce9179b 100755 --- a/src/passport/card_MRZutile.py +++ b/src/passport/card_MRZutile.py @@ -1,18 +1,18 @@ from mrz.generator.td1 import TD1CodeGenerator -first_name = "eryan" -last_name = "dai" +first_name = "kele" +last_name = "mi" document_number = "XKJ0WSK30" -birth_day = "951211" -# sex = "F" -sex = "M" +birth_day = "991125" +sex = "F" +# sex = "M" # optinal_data = "MFMLMANK<<<