From 220e91173146a9fe1431faddac9530bdb4e9816b Mon Sep 17 00:00:00 2001 From: Miguel Date: Wed, 5 Feb 2025 15:04:43 +0100 Subject: [PATCH] Intento fallido de separar los emails reenviados --- TEST.eml | 1317 ----------------- .../__pycache__/email_parser.cpython-310.pyc | Bin 7200 -> 7446 bytes .../forward_handler.cpython-310.pyc | Bin 0 -> 2999 bytes utils/email_parser.py | 177 ++- utils/forward_handler.py | 144 ++ 5 files changed, 230 insertions(+), 1408 deletions(-) delete mode 100644 TEST.eml create mode 100644 utils/__pycache__/forward_handler.cpython-310.pyc create mode 100644 utils/forward_handler.py diff --git a/TEST.eml b/TEST.eml deleted file mode 100644 index e0bffa2..0000000 --- a/TEST.eml +++ /dev/null @@ -1,1317 +0,0 @@ -From: Miguel Angel Vera - Vetromeccanica S.r.l. -To: Miguel Angel Vera - Vetromeccanica S.r.l. -Subject: TEST Tables -Thread-Topic: TEST Tables -Thread-Index: AQHbd7PepN5tf2wDskaE081T3aMPKQ== -Date: Wed, 5 Feb 2025 09:53:55 +0000 -Message-ID: - -Content-Language: es-ES -X-MS-Has-Attach: -X-MS-TNEF-Correlator: -X-MS-Exchange-Organization-RecordReviewCfmType: 0 -msip_labels: -Content-Type: multipart/alternative; - boundary="_000_AS8PR08MB682139DC577AE00699D695149CF72AS8PR08MB6821eurp_" -MIME-Version: 1.0 - ---_000_AS8PR08MB682139DC577AE00699D695149CF72AS8PR08MB6821eurp_ -Content-Type: text/plain; charset="Windows-1252" -Content-Transfer-Encoding: quoted-printable - -Allego le email che riassumono il funzionamento del batch handling. Riporto= - sotto la parte principale. - - - -We confirm the following code sas per attached e-mail) are matching with wh= -at we agreed back in May therefore we can process P/N (customer article) b= -ut ALPLA should write in our supervision PLC a list where customer article = -is associated to AV, AV Desc, Product Family as agreed. - -This list will be constantly editable by ALPLA on a persistent memory as ag= -reed in order for Vetromeccanica to handle new customer articles except for= - new formats (new bottle shapes) which will require new recipes and commis= -sioning first. - -As per now we only have an excel list dated May 2020 which we cannot use fo= -r batch handling. - -ALPLA AV - -Blank Bottle P/N - -184 - -2638879 - -253 - -2688129 - -102 - -2638876 - - - - - - - - - - - -Agreed: - - * AV (must be forwared to Autefa, must be shown on your HMI=92s) - * AV Desc (must be shown on you HMI=92s) - * Product Family (must be used by Vetro to select right Recipe) - * Customer article Number (used by Vetro to find matching AV) - - - - - -HENKEL - -Alpla - -Vetromeccanica - -AUTEFA - -Before Changeover - -Sends IDH_BTL_NEXT number to be validated - - - - - - - -Data_To_EbConvey[38] - - - - - - - - - -Validates IDH_BTL_NEXT is valid number and send acknowledge - - - - - - - -Data_From_EbConvey[0].2 - - - - - -Step 1 - -Operator manually selects "Line Clearance" (?) and send signal "0" on "Calc= -ulatedBottlesRemainingToFill". - - - - - - - - - -Data_To_EbConvey[23] - - - - - - - -Step 2 - - - -Stops taking bottles out of trays and sends what is already on the tables a= -nd conveyors. - -Emptying Merger and Line - -Stops taking bottles out of trays and sends what is already on the tables a= -nd conveyors. - - - - - -N/A - -TG10 Send 0 in Bottles for Actual Batch - - - -Step 3 - -Operator verifies line is empty and sends "changeover request" signal after= - last bottle goes thorugh filler. - - - - - - - - - -Data_To_EbConvey[0].0 - - - - - - - -Step 4 - - - -Operator verifies line is empty and sends "line is busy with changeover", c= -onfirming on Popup screen - - - - - - - - - -Data_From_EbConvey[0].0 - - - - - -Step 5 - - - -Starts changeover - -Starts changeover - - - - - - - -Data_From_EbConvey[0].0 - -Data_From_EbConvey[0].0 - -Step 6 - - - - - -Ends changeover. Sends signal "Changeover is finished and ready. - -Ends changeover. Sends signal "Changeover is finished and ready. - - - - - - - -Data_From_EbConvey[0].1 - -Data_From_EbConvey[0].1 - - - - - - - - - - - - - - - - - -Step 7 - -Send Reset counters signal - -Reset Counters - - - - - -Data_To_EbConvey[0].1 - - - - - - - -Step 8 - -Send new value on "CalculatedBottlesRemainingToFill". - - - - - - - -Data_To_EbConvey[23] - - - - - - - -Step 9 - -Send Changeover Complete. To be considered "Production Ready" - -Starts conveying bottles. - -Finish Chanover Cycle - - - - - -Data_To_EbConvey[0].2 - - - - - - - - - - ---_000_AS8PR08MB682139DC577AE00699D695149CF72AS8PR08MB6821eurp_ -Content-Type: text/html; charset="Windows-1252" -Content-Transfer-Encoding: quoted-printable - - - - - - - -

-Allego le email che riassumono il funzionamento del batch h= -andling. Riporto sotto la parte principale.

-

- 

-

-We confirm the following code sas per attached e-mail) a= -re matching with what we agreed back in May therefore we can process P/N (c= -ustomer article)  but ALPLA should - write in our supervision PLC a list where customer article is associated t= -o  AV, AV Desc, Product Family as agreed.

-

-This list will be constantly editable by ALPLA on a pers= -istent memory as agreed in order for Vetromeccanica to handle new customer = -articles except for new formats (new - bottle shapes) which will  require new recipes and commissioning firs= -t.

-

-As per now we only have an excel list dated May 2020 whi= -ch we cannot use for batch handling.

- - - - - - - - - - - - - - - - - - - -
-

-ALPLA AV

-
-

-Blank Bottle P/N

-
-

-184

-
-

-2638879

-
-

-253

-
-

-2688129

-
-

-102

-
-

-2638876

-
-

- 

-

- 

-

- 

-

- 

-

- 

-

-Agreed:

-
    -
  • -AV  (must be forwared to Autefa, must be shown on you= -r HMI=92s)
  • -AV Desc  (must be shown on you HMI=92s)
  • -Product Family (must be used by Vetro to select right Reci= -pe)
  • -Customer article Number (used by Vetro to find matching AV= -)
-

- 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

- 

-
-

-HENKEL

-
-

-Alpla

-
-

-Vetromeccanica

-
-

-AUTEFA

-
-

-Before Changeover

-
-

-Sends IDH_BTL_NEXT number to be validated

-
-

- 

-
-

- 

-
-

- 

-
-

-Data_To_EbConvey[38]= -

-
-

- 

-
-

- 

-
-

- 

-
-

- 

-
-

-Validates IDH_BTL_NEXT is valid number and se= -nd acknowledge

-
-

- 

-
-

- 

-
-

- 

-
-

-Data_From_EbConvey[0].2

-
-

- 

-
-

- 

-
-

-Step 1

-
-

-Operator manually selects "Line Clearanc= -e" (?) and send signal "0" on "CalculatedBottlesRemaini= -ngToFill".

-
-

- 

-
-

- 

-
-

- 

-
-

- 

-
-

-Data_To_EbConvey[23]= -

-
-

- 

-
-

- 

-
-

- 

-
-

-Step 2

-
-

- 

-
-

-Stops taking bottles out of trays and sends w= -hat is already on the tables and conveyors.

-
-

-Emptying Merger and Line

-
-

-Stops taking bottles out of trays and sends w= -hat is already on the tables and conveyors.

-
-

- 

-
-

- 

-
-

-N/A

-
-

-TG10 Send 0 in Bottles for Actu= -al Batch

-
-

- 

-
-

-Step 3

-
-

-Operator verifies line is empty and sends &qu= -ot;changeover request" signal after last bottle goes thorugh filler.

-
-

- 

-
-

- 

-
-

- 

-
-

- 

-
-

-Data_To_EbConvey[0].0

-
-

- 

-
-

- 

-
-

- 

-
-

-Step 4

-
-

- 

-
-

-Operator verifies line is empty and sends &qu= -ot;line is busy with changeover", confirming on Popup screen -

-

- 

-
-

- 

-
-

- 

-
-

- 

-
-

-Data_From_EbConvey[0].0

-
-

- 

-
-

- 

-
-

-Step 5

-
-

- 

-
- -

-Starts changeover

-
-

-Starts changeover

-
-

- 

-
-

- 

-
-

- 

-
-

-Data_From_EbConvey[0].0

-
-

-Data_From_EbConvey[0].0

-
-

-Step 6

-
-

- 

-
-

- 

-
-

-Ends changeover. Sends signal "Changeove= -r is finished and ready.

-
-

-Ends changeover. Sends signal "Changeove= -r is finished and ready.

-
-

- 

-
-

- 

-
-

- 

-
-

-Data_From_EbConvey[0].1

-
-

-Data_From_EbConvey[0].1

-
-

- 

-
-

- 

-
- -

- 

-
-

- 

-
-

- 

-
-

- 

-
- -

- 

-
-

- 

-
-

-Step 7

-
-

-Send Reset counters signal

-
- -

-Reset Counters

-
-

- 

-
-

- 

-
-

-Data_To_EbConvey[0].1

-
-

- 

-
-

- 

-
-

- 

-
-

-Step 8

-
-

-Send new value on "CalculatedBottlesRema= -iningToFill".

-
- -

- 

-
-

- 

-
-

- 

-
-

-Data_To_EbConvey[23]= -

-
-

- 

-
-

- 

-
-

- 

-
-

-Step 9

-
-

-Send Changeover Complete. To be considered &q= -uot;Production Ready" 

-
-

-Starts conveying bottles.

-
-

-Finish Chanover Cycle

-
-

- 

-
-

- 

-
-

-Data_To_EbConvey[0].2

-
-

- 

-
-

- 

-
-

- 

-
-

- 

-
-
-
- - - ---_000_AS8PR08MB682139DC577AE00699D695149CF72AS8PR08MB6821eurp_-- diff --git a/utils/__pycache__/email_parser.cpython-310.pyc b/utils/__pycache__/email_parser.cpython-310.pyc index 15fe621948d32057640df058fe1153381e8c0b3b..f6974d3022b42abec58cc8962a3d42126e8e08a4 100644 GIT binary patch delta 2445 zcmZ`*Pi!1#6`yZr_W$he>_2rP1lJ{2qi6v<+i4PVww_YBezSV(XiiV@7S}m zn>XWZyqd)kPC*n3jy{PCs<6c=5>i$9+CwXGC|rm*p*e8i2I7JQ*NXRM-J~sLSM&Sc zy#Mcg^SznFXMT05(9h?y3jQ9rzuoi~?-l0QgL`LhSy|WYmD(kyaD_9s+%Cs!rCs4U zZroMcRi5VsTx-0@OmyEG$}n#a7wu9B*Dt~D{(WsH7*7G+Z z&M1yLX^#ApImZsEr|aD6!z-xM)>Zk}G|EOCzyuOZ?@1bN<8LJ0uG$qrO@9(`EDu?MKD}4>L!~I5&DV%<<&53TpxK_Sx774Y(2-Nw>sqa=-z zjUX3hL$Wg*YAxZj66xFy1YAM#rBQ~4;Fst)kFqg2aBatfZ_B^cF7J)b zWmk9nj_vV&^W*^eO%TGn<=DLMH-*=n9Bu~{o*bo`_-upU8da`=(-VLi7tWubFfaZ6 zLyR{vzTuGt#O9;w=XnK&imgPHfrjWgG&uygly>G}EVv?{om!ZlgD&!c4#s32evhcr z@@G@c1`Q#2k>DypgJ7NDn*8I`!X7wCM`_`4f9M2Vp!Y4jK#(MX?%A0Cqv^6n5;|u< z2QErFo-ZJP4BoVA@Hz=-*1&awn*`egly=-_0HS<^c7duegoiDQrznm-ce*KmGu_&w zq1QI>><5;=^MftX3E&j5Dh~^{2HZxZ|QhUf{;(mcK2#Kf#dp1 zCm$bH7oR;B2Pw6^oeo5Au;I8OQv3d7OCP@cAl{;Qhi{O}_@SWKpXkZtRf4=Iz1-{b b9apT-v*y#M1a^h~*bw}{VRLg@-l+Z;EHGGo delta 2203 zcmZ`)-;Wbj6rMYsw$panPTTEnySrFeaG?teA_fr_(3KDngoPz21yhFJ*=?70+PyPN zVKZYl)R_1{T)2rZMzR}TBr!_T#6%P0i!a9iV2lsG;+u-b5RB(ccVR(obH97${JP&g zbMKjt_Ac#-)uYj{06#i$XL@w|&DbFM?&j7jlccH^R4PzXO%xI|Oe60Jg(OvI6y_9- z(KyUKRHX@+(=C>Gc-f9FlQ;56!QJI$U^5fIYUT<|0eDueSC-S zB#HBuZzIWe?)YjX(g!@42N^!#&ygJ8=YO6IcQpUoWM>YI8vyd6D{g;*Z36KwVH=;s zfaHe8rgcNV+F%Fv^ZS9Z!)PwV%eqE&W)`h_(Y#z~bl1pZWCnv0MYCL=t5VNctX2&D zMNpd(fGqU}*|ttKIPN3s_!nVk{btbK^||u2ZWS&4s>P6)>#xqtnbut&+rp1W2IAdK zfPswwP<|zHeKYFvBHQA*H!IXCcX>SRJH>O#)=^xa;Yw^5!fu3D5T16fp=vL0DhJ3Y z|5Dim<-M3fc&cJ(-ArBT z-X^@Ft3@PG)QUBYU8eP>agd9#8{}wbG4>fDF}`0Ngws(_-%LKelYEe+_*d$1e-Etg z`fBDh^B8%R>6VB81+gFU-o!z2wsSTyM$Szj-DmRn3ksXFw4%mJ<;s;ho5bDt^jg&o z)^x+vX7qn0#ub$YtCw_BV?}WNod1$sH*x{=B@tltmjWRI<8>Hj87$L+BQBEp6#qVb zzIDhJmgHsOknoP;gzfNBXj!0v%OV@6K_^56gUs$W0^qZvcS0Zsmj(9IE4|OPse~Sx>gwHY&MHx`?ywL{M1=M#X znS@s|B|Rw&`NmZuEyPF?Llz*_&&tLp2_RTZ`T|6J{0~2rBI56oqCOfVk_Zux*hD9k zN|L}qB)*?-=*dI|Q5-<%=ZAXU-~SqjuE=yZfUmJsb_3H4nzqSG=re`jM;BkkunIn- z-pq$t1+^Cecp^QXUeg*;PXW?|o9S&biurzair*jDKxVlzSZ>LdUTaKMs=6!I&F*=4 z{MlG@GO*`)mG;uD`I2>8Fj!rAtX8LURs8^gClkMW@DjZL>fC)XP;^mN<&+$iCuDz4 IR+RJq0IKi!od5s; diff --git a/utils/__pycache__/forward_handler.cpython-310.pyc b/utils/__pycache__/forward_handler.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3892e800f6d65bb9ec606b555357f0e0e16f8ef6 GIT binary patch literal 2999 zcmaJ@O>7*u6(%{q`yg0N*Ob8>q4P3}!=X4Xk|{ zNhXsngLvm=IB?ljK3plTMOF zDTCh0c>nxN@#bVW7+btVYckQ)*_^L8X1*pa0(xOQ>729P+Yro5^S4|S{ORAbPMX9! zELe-;9S${lwBPGFe-LrXXwWUgn)BN~G<~@n0cR}xB#CG~=k3U?NwPeZJd*Q@?_9jv zZG5mgGlkj@Z|0>VEMC5=4X0miMwh|s#g(@j{rYvDcc-mtlTGWjJ9##1G+A)a01r#= zn*e2g(ABSRuuM){ORaJI(~e`mW`bpuO;viw;5a}l-+TJiA;&j69VX`-FKzTb+-339 zu)las^s;${Uopwq_V#qH#f{!BlQXB5wznZhZJR9Co*4U!vB~bqQ-QA!Qvlv{ivEMGAig{Pl%( z2f=a)HA}Ug z*cK~S(mak*vAQy~J6~MoG3gA5b4JPUE?G?4tj75ZI+pF4$K#xoN3M`^(Ovb2wtCk`C0>x5*ScMF_0n-k(27u22;7VfU3^CyD zkIJQX`E`^BxFdjZUtw`oTGTPP-8wy}NPFZcgFG^*sw!Vq)saixeOzi|@Zi3S25|Xi z_~Q$Th%K-MUq{+*9hX6B_j`* z$)nhC(q#R>BVq)r9w}Rl%1xb^@)K#h9uo;fW;SAcPovcU_~1Uz&= zVKW8`_JT5PSdd0Rk^zppQJg&eTLvmA`y>KpD}AnMTpi=rf@!#8gt>jVYlH`)|H?Y+ zCP0Le1x+R(bMqKDvEk?nKN!%?Hj8&6Zc?!{(tbpD%}pfw=USZSJcv01-pRke(#t_( zk-uUL4M*@(AoS92TGyJb^(m(LvgviYk&Jh=)8Toq;3tf>VJvP)Mq63_Kv!B1V^+u{ z&$LtUB$L_#258jB3;T)DtCTBkLwC8*(2aYH7kTKWNyZ`(p4@@|H^#*rTm}CzScXoi z!57Sw7mO>+UVth;3*Bo_%70PYu>pgCLd(cyQpdJc#~|@yiB9rg+x_9T?=1h@KX(e( z!2_`fb`7G-*Z*$OJ7N4hBL6FAZPdnlL~=3Sk6L+Ot(4Ty&DrYZZVtDaSeejFyHptO zApBP&{-zOu5hjee(8`;}@UZr;fRA3vRt?z;b6!4#VuGk+-*S(;9=zZBXX>y2A7!;9 AE&u=k literal 0 HcmV?d00001 diff --git a/utils/email_parser.py b/utils/email_parser.py index 6ba70e9..833c686 100644 --- a/utils/email_parser.py +++ b/utils/email_parser.py @@ -9,6 +9,7 @@ from bs4 import BeautifulSoup from email.utils import parsedate_to_datetime from models.mensaje_email import MensajeEmail from utils.attachment_handler import guardar_adjunto +from utils.forward_handler import extract_forwarded_messages import tempfile import os @@ -183,96 +184,6 @@ def procesar_eml(ruta_archivo, dir_adjuntos): print(f"Error al abrir el archivo {ruta_archivo}: {str(e)}") return [] -def procesar_eml_interno(mensaje, dir_adjuntos): - """ - Procesa un mensaje de email, ya sea desde archivo o adjunto - """ - mensajes = [] - - try: - remitente = mensaje.get('from', '') - fecha_str = mensaje.get('date', '') - fecha = _parsear_fecha(fecha_str) - - # Get subject from email headers first - subject = mensaje.get('subject', '') - if subject: - # Try to decode if it's encoded - subject = str(email.header.make_header(email.header.decode_header(subject))) - - contenido = "" - adjuntos = [] - tiene_html = False - - # First pass: check for HTML content - if mensaje.is_multipart(): - for parte in mensaje.walk(): - if parte.get_content_type() == "text/html": - tiene_html = True - break - else: - tiene_html = mensaje.get_content_type() == "text/html" - - # Second pass: process content and attachments - if mensaje.is_multipart(): - for parte in mensaje.walk(): - content_type = parte.get_content_type() - - try: - if content_type == "text/html": - html_content = _get_payload_safely(parte) - if html_content: - part_subject, text = _html_a_markdown(html_content) - if not subject and part_subject: - subject = part_subject - if text: - contenido = text - elif content_type == "text/plain" and not tiene_html: - text = _get_payload_safely(parte) - if text: - contenido = text - elif content_type == "message/rfc822": - # Procesar email adjunto - mensajes_adjuntos = _procesar_email_adjunto(parte, dir_adjuntos) - mensajes.extend(mensajes_adjuntos) - elif parte.get_content_disposition() == 'attachment': - nombre = parte.get_filename() - if nombre and nombre.lower().endswith('.eml'): - # Si es un archivo .eml adjunto - mensajes_adjuntos = _procesar_email_adjunto(parte, dir_adjuntos) - mensajes.extend(mensajes_adjuntos) - else: - # Otros tipos de adjuntos - ruta_adjunto = guardar_adjunto(parte, dir_adjuntos) - if ruta_adjunto: - adjuntos.append(Path(ruta_adjunto).name) - except Exception as e: - print(f"Error procesando parte del mensaje: {str(e)}") - continue - else: - if mensaje.get_content_type() == "text/html": - html_content = _get_payload_safely(mensaje) - if html_content: - part_subject, contenido = _html_a_markdown(html_content) - if not subject and part_subject: - subject = part_subject - else: - contenido = _get_payload_safely(mensaje) or "" - - # Solo agregar el mensaje si tiene contenido útil - if contenido or subject or adjuntos: - mensajes.append(MensajeEmail( - remitente=remitente, - fecha=fecha, - contenido=contenido, - subject=subject, - adjuntos=adjuntos - )) - - except Exception as e: - print(f"Error procesando mensaje: {str(e)}") - - return mensajes def _parsear_fecha(fecha_str): try: @@ -292,4 +203,88 @@ def _parsear_fecha(fecha_str): return datetime(int(año), mes_num, int(dia), int(hora), int(minuto)) except: pass - return datetime.now() \ No newline at end of file + return datetime.now() + +def procesar_eml_interno(mensaje, dir_adjuntos): + """ + Procesa un mensaje de email, ya sea desde archivo o adjunto + """ + mensajes = [] + + try: + remitente = mensaje.get('from', '') + fecha_str = mensaje.get('date', '') + fecha = _parsear_fecha(fecha_str) + + # Get subject from email headers first + subject = mensaje.get('subject', '') + if subject: + subject = str(email.header.make_header(email.header.decode_header(subject))) + + contenido = "" + adjuntos = [] + contenido_html = None + + # First pass: check for HTML content and extract it + if mensaje.is_multipart(): + for parte in mensaje.walk(): + content_type = parte.get_content_type() + + try: + if content_type == "text/html": + html_content = _get_payload_safely(parte) + if html_content: + contenido_html = html_content + elif content_type == "text/plain" and not contenido_html: + contenido = _get_payload_safely(parte) or "" + elif content_type == "message/rfc822": + mensajes_adjuntos = _procesar_email_adjunto(parte, dir_adjuntos) + mensajes.extend(mensajes_adjuntos) + elif parte.get_content_disposition() == 'attachment': + nombre = parte.get_filename() + if nombre and nombre.lower().endswith('.eml'): + mensajes_adjuntos = _procesar_email_adjunto(parte, dir_adjuntos) + mensajes.extend(mensajes_adjuntos) + else: + ruta_adjunto = guardar_adjunto(parte, dir_adjuntos) + if ruta_adjunto: + adjuntos.append(Path(ruta_adjunto).name) + except Exception as e: + print(f"Error procesando parte del mensaje: {str(e)}") + continue + else: + if mensaje.get_content_type() == "text/html": + contenido_html = _get_payload_safely(mensaje) + else: + contenido = _get_payload_safely(mensaje) or "" + + # Process HTML content if available + if contenido_html: + part_subject, text = _html_a_markdown(contenido_html) + if not subject and part_subject: + subject = part_subject + contenido = text + + # Process forwarded messages from the markdown content + if contenido: + print(f"\nBuscando mensajes reenviados en contenido ({len(contenido)} chars)") + print("Primeros 200 chars:", contenido[:200]) + contenido_principal, mensajes_reenviados = extract_forwarded_messages(contenido) + print(f"Encontrados {len(mensajes_reenviados)} mensajes reenviados") + contenido = contenido_principal + mensajes.extend(mensajes_reenviados) + + # Solo agregar el mensaje principal si tiene contenido útil + if contenido or subject or adjuntos: + mensajes.insert(0, MensajeEmail( + remitente=remitente, + fecha=fecha, + contenido=contenido, + subject=subject, + adjuntos=adjuntos + )) + + except Exception as e: + print(f"Error procesando mensaje: {str(e)}") + + return mensajes \ No newline at end of file diff --git a/utils/forward_handler.py b/utils/forward_handler.py new file mode 100644 index 0000000..d17b407 --- /dev/null +++ b/utils/forward_handler.py @@ -0,0 +1,144 @@ +# utils/forward_handler.py +import re +import os +from datetime import datetime +from email.utils import parseaddr +from models.mensaje_email import MensajeEmail + +# Patrones de inicio de mensaje reenviado en diferentes idiomas +FORWARD_PATTERNS = [ + r"[-]{3,}\s*Messaggio originale\s*[-]{3,}", # Italiano + r"[-]{3,}\s*Original Message\s*[-]{3,}", # Inglés + r"[-]{3,}\s*Mensaje original\s*[-]{3,}", # Español + r"[-]{3,}\s*Message d'origine\s*[-]{3,}", # Francés + r"[-]{3,}\s*Ursprüngliche Nachricht\s*[-]{3,}", # Alemán + # Variantes más flexibles + r"[-]{3,}\s*Forwarded message\s*[-]{3,}", + r"[-]{3,}\s*Mensaje reenviado\s*[-]{3,}", + r"[-]{3,}\s*Messaggio inoltrato\s*[-]{3,}", + # Patrones con > que suelen aparecer en texto plano + r"(?m)^>\s*[-]{3,}\s*Messaggio originale\s*[-]{3,}", + r"(?m)^>\s*[-]{3,}\s*Original Message\s*[-]{3,}" +] + +# Patrones de headers en diferentes idiomas +HEADER_PATTERNS = { + 'from': [ + r"Da:\s*(.*)", # Italiano + r"From:\s*(.*)", # Inglés + r"De:\s*(.*)", # Español + r"Von:\s*(.*)", # Alemán + r"De :\s*(.*)" # Francés + ], + 'date': [ + r"Inviato:\s*(.*)", # Italiano + r"Sent:\s*(.*)", # Inglés + r"Enviado:\s*(.*)", # Español + r"Gesendet:\s*(.*)", # Alemán + r"Envoyé :\s*(.*)" # Francés + ], + 'subject': [ + r"Oggetto:\s*(.*)", # Italiano + r"Subject:\s*(.*)", # Inglés + r"Asunto:\s*(.*)", # Español + r"Betreff:\s*(.*)", # Alemán + r"Sujet :\s*(.*)" # Francés + ] +} + +def extract_forwarded_messages(contenido): + """ + Extrae mensajes reenviados del contenido del email + Retorna una lista de objetos MensajeEmail + """ + mensajes = [] + + # Crear el patrón de división combinando todos los patrones de reenvío + split_pattern = '|'.join(f"({pattern})" for pattern in FORWARD_PATTERNS) + + # Dividir el contenido usando el patrón combinado + partes = re.split(split_pattern, contenido) + + # El primer elemento es el contenido original del email + contenido_original = partes[0].strip() + + # Procesar cada parte que coincide con un patrón de reenvío + for i in range(1, len(partes), len(FORWARD_PATTERNS) + 1): + # Encontrar qué patrón coincidió + patron_encontrado = next((p for p in partes[i:i+len(FORWARD_PATTERNS)] if p), None) + if patron_encontrado and i + len(FORWARD_PATTERNS) < len(partes): + contenido_reenviado = partes[i + len(FORWARD_PATTERNS)].strip() + if contenido_reenviado: + mensaje = _procesar_contenido_reenviado(contenido_reenviado) + if mensaje: + mensajes.append(mensaje) + + return contenido_original, mensajes + +def _procesar_contenido_reenviado(contenido): + """ + Procesa el contenido de un mensaje reenviado y extrae la información relevante + """ + # Extraer headers + remitente = None + fecha_str = None + subject = None + cuerpo = contenido + + # Buscar headers al inicio del mensaje + lineas = contenido.split('\n') + headers_encontrados = 0 + i = 0 + + while i < len(lineas) and headers_encontrados < 3: + linea = lineas[i].strip() + + # Buscar remitente + if not remitente: + for pattern in HEADER_PATTERNS['from']: + match = re.match(pattern, linea) + if match: + remitente = match.group(1).strip() + headers_encontrados += 1 + break + + # Buscar fecha + if not fecha_str: + for pattern in HEADER_PATTERNS['date']: + match = re.match(pattern, linea) + if match: + fecha_str = match.group(1).strip() + headers_encontrados += 1 + break + + # Buscar asunto + if not subject: + for pattern in HEADER_PATTERNS['subject']: + match = re.match(pattern, linea) + if match: + subject = match.group(1).strip() + headers_encontrados += 1 + break + + i += 1 + + # Si encontramos headers, el cuerpo comienza después de ellos + if headers_encontrados > 0: + cuerpo = '\n'.join(lineas[i:]).strip() + + # Si no tenemos la información mínima necesaria, retornar None + if not (remitente or fecha_str or cuerpo): + return None + + # Crear el objeto MensajeEmail + try: + return MensajeEmail( + remitente=remitente or "Remitente Desconocido", + fecha=fecha_str or datetime.now(), + contenido=cuerpo, + subject=subject, + adjuntos=[] + ) + except Exception as e: + print(f"Error creando mensaje reenviado: {str(e)}") + return None \ No newline at end of file