From 36e1a225e656e8375a1865a07f5277a73fb09a42 Mon Sep 17 00:00:00 2001 From: Dennis Eckerskorn Date: Tue, 14 Jan 2025 21:54:03 +0100 Subject: [PATCH] Added Mail interface and POP client, pop connections still needs to be closes when program is finished --- .idea/MutiFunctionProgramProject.iml | 6 +- .idea/misc.xml | 4 +- src/resources/db_email/emails.db | Bin 0 -> 16384 bytes .../__pycache__/Radio_Player.cpython-313.pyc | Bin 2782 -> 2786 bytes .../email_client_pop.cpython-313.pyc | Bin 0 -> 12138 bytes .../__pycache__/scrapper.cpython-313.pyc | Bin 7535 -> 8004 bytes .../__pycache__/tetris_game.cpython-313.pyc | Bin 8543 -> 9097 bytes .../threads_manager.cpython-313.pyc | Bin 10253 -> 12209 bytes src/services/email_client_pop.py | 170 +++++++++++- .../centered_window.cpython-313.pyc | Bin 17073 -> 22060 bytes src/ui/centered_window.py | 261 ++++++++++++------ 11 files changed, 341 insertions(+), 100 deletions(-) create mode 100644 src/resources/db_email/emails.db create mode 100644 src/services/__pycache__/email_client_pop.cpython-313.pyc diff --git a/.idea/MutiFunctionProgramProject.iml b/.idea/MutiFunctionProgramProject.iml index 07abf20..d8b3f6c 100644 --- a/.idea/MutiFunctionProgramProject.iml +++ b/.idea/MutiFunctionProgramProject.iml @@ -2,11 +2,7 @@ - + - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index db8786c..1d3ce46 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,7 @@ - - + \ No newline at end of file diff --git a/src/resources/db_email/emails.db b/src/resources/db_email/emails.db new file mode 100644 index 0000000000000000000000000000000000000000..3e8da67fb8a29aad60f184d98383738765b75f56 GIT binary patch literal 16384 zcmeI#&rZTH90%}r5J^lR-Ux|@CR`v8FTQ|N8?!hD&XUN93|5mF{skV|MM^RS~ zywyQf=FIdT==FJ?MTMnjX;``%mkY(udb4p``PY4yUY!{IR8K%Qz_3 zYP=KUOa_#xc0)ax=}}*Y$!;czlpL&#yrHhs%<8hhbH!4LuNLbyW9#^7Bvy#O{kkxn z+1JxQ;ib#e!Cd>@oy4EHIVJrl5P$##AOHafKmY;|fB*y_009W>u>kV_J-%EV4FV8= u00bZa0SG_<0uX=z1R(Gwfczi70t6rc0SG_<0uX=z1Rwwb2tZ)}1wH`Yrl<`7 literal 0 HcmV?d00001 diff --git a/src/services/__pycache__/Radio_Player.cpython-313.pyc b/src/services/__pycache__/Radio_Player.cpython-313.pyc index a2bbd35ba4aafa2b2f8779a4a0101fae2335ef09..74cb7f7c83c2469ed519a07837c46bfcae77c577 100644 GIT binary patch delta 56 zcmca7`bd=bGcPX}0}xE#)Ru0uk++AD(RuSM#`_$sQpH8ddXuZTLIjMzvnVp!eqsO; IMS?)30PKJdwg3PC delta 52 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 literal 0 HcmV?d00001 diff --git a/src/services/__pycache__/scrapper.cpython-313.pyc b/src/services/__pycache__/scrapper.cpython-313.pyc index 6c9e88baaacc0eaf766eeaf281ab66e42967797c..dc095e187f816fad1b5fff734ddb017892ecf041 100644 GIT binary patch delta 2370 zcmZuzZERat89wKJ*f;jIjI$;nUHPTQGXBSA7u za+Wa3S*Ha4Dj`fQ(WJ6*X~LDH9rfIKrWaTB>bauv;>OQ)YKH+Dq91Nf&?$OiDcNt!QR;-n)r9vHN>{wTqO(Q9F zBtx&fz(0k; zT7C*PRS#^dURbx-q&!(vpxEaD12?hf6!HY(mZ6C%&C^4?Kepntge>tf%$Ae#)Jf99 z)ZQ!_JZA}b7JG@?e~*Ae2)OcepBatdd|p1E_kZ6ddH9_Aa7@^+_yTfoY*G@EmX{)5 zua;NJMJU<=(6VG9V@+OESrBJpoI@5s=*TM-y~x;*X@9$+H}p<_Nv{_gtWtP^tz9TI z$}C|9je3#QJ9d*Ry-+KbbT-NZ?sC=8S-nsxS6?&=^|iw4a#CPpJS5^M2A7zou_SlE z@2rO`<7gQ_g?m=*kfOGga8n7l6|JdgEoFF1QP=yg_P3QtQ;D>dk)|@zQbxBSqG(6A zR99P#Hq~fbO*GZSyXV@YQ_az-_l%Z$0%Pk>UVX9~|8cz?n{LLY|EN9!7nMZn4b1uL zWU0q*4t0!cz^0KLQdj9k;62s9&*=fte?j(C4;3 z=sdh^``&0A=jw83vLYGvwVKI8mqM}DsN;D_+Y`Yq1NGuEJC1z}u0)OV0BiQtVbk?K zx-OBwDvsYfkG7O4JU>^YSJdqTQesUd)>7i|Yx_E#hO=r&SQOz2c?4cne@p)a&pLhr zi}H|&!rZ*;n5A?aGW}PCFKlqL|1e#E-Tt)Qlp9wX8-ydS(*-Ux_85nUIhflSK`W`4 zl3{7II||c{+8W9V3&2{V#?El3YznL+@f+iZ%uo<;OJ+0m#U6%Ru8?Jc2cL$2xiZuT zj|>#up1`%twak_`cx_>0;oWr0I|)ANW=yBfsY4}C2q{>C%o zn5X0uEispou1hm%e7JGUhxn;;kYma=cij4EMn?N4wat%-H|+!Saq(tco*&zM&3&Hw zj^PT-w6jOKb%Mi5uzQo=I)&y!Qf5Bxn&I#`hgl9;grwyl59}M@4@7L-f%{K`KJXJtR1Gu4|D>%p1N35vWV#>r9`7icysDWX=Y7VLJ%KSDDQ2%M#-p%nPr zgcn~(I=d=2^<;|ZY4oxvhZqNqLj*1aBMZaaJW!UkTa2V~UBA$=)rw57n)S`hC*#$K z6*-uivZB>ZZ?|NzBd`_Jf-j@nxQB3wY&+fSm8+Gua~OYM3H{RjWsLND4t#$=_s5|J zhTa~UJxM-%Jb`%qq%9j0Z&;n#uy`XZXJea6*heve(ZRtq-$=(fQ?Ik~a-*)#F}B9c z+>iY2p>4E3^P8Lz#;y{(O|fJO}dt za`~_$93DjI_zDHA^Y!vdp}4;uqflU+0mik|J!bsiF%EXT44%QuWJd_eV>{C>`S?!4 zgOG~I>7B=@O@3%6?2)td9}oDga5R@aLf;7fl`~`7SZQ}<{{dB_5vu?I delta 2078 zcmZ8iU2Ggh67HVA9na2=*Iv)=?09!Gwl|K~PJ%{|GzOEuU))AXq`chMdSXN7ITB!O>)H)$OHE-q*VDUdvO`Y>yPhomeWhbN>% zxhVJH{f)ysx!T;mq*~Xr&MT!8(voUlHEsA_Iuf~}=f1mMZ^69W)n$|Ow$|S#p3fWo zTgCHRWiF3XOk{N^{4Mh5>%;I~q8WZN>fn2MJ(b~*V8c5~j2?v#l$<__cHtON;YHd+ zOK>x3Y9+db7R936E5d6^#&F2*?O;tRsc;LWS3rxLLneh$j2Dh#q-Ql3)E88EB{H5b zh=Jl)*re+R5`TGtGEs4l%~T5l>*TxGdJgboq;OniDV#eTf_iRVo@3MH!))eQd2WhX zs8#kKVQyt2htHVl`nfIU`cf#Q|I(w*Ue=;^Rb>eJ-D3TvY6j;cHrzE z{*_1JF8Epk*E;KJwzp)W{hA49Ec#e((DkR>stbprDcB!vhsUE&Zg+U# zAoj~~mHmo$lyl(E%$EHcA(4r(npQ$BYesuu4WSR#nuU=& zxVX0s5ppl7XmWG{>;I{7IEvh9)QzkicY{;^Au-dZ!#VE zB&_QkeAH}H5ep11C#|T&_l0A%lEVfg7qxB@HW}C{$f8t|c>l=Ca!DSk=R2E>U+XYb zJ)K%h8}JH9e43~UQYd7p39{8{-*=Ia7BY(5h9g2Myh&7*Q4=1w+UOd1)f%Sv!dKSk zEeUQ=(8IE1BV1-#gdD;egbe(Y&NjB7=pcjz5n*kx(N4DGS+G397(!SG5iZq^rq)r| zCV1F(j7PZI`+~zf`P68HzuIrf|6N1s6EM}hKJ!dwKq065^BgZI+CYbRA#Mz`i5J@B zfsQk8Hk}r!^4`XbB z!)lXb8_=7Doo#m3WgCCjP@h9{tc7`UQ-%{%?IUB3m^lxW5n$e++CNoXp)mfvg7dBXjPY z%VaLsLi{#tp!OY`nV6e)cd);36KumCwx^pSrSuw!UnBK5NcIMCu94&olKNI16sWaA I5W}!P1CWc~L;wH) diff --git a/src/services/__pycache__/tetris_game.cpython-313.pyc b/src/services/__pycache__/tetris_game.cpython-313.pyc index 6610ecae31a1d1a6947a54cee30d3cdd41603e86..3f8f3af9aea964867ea1ac1f03b98c36ed8c0842 100644 GIT binary patch delta 2430 zcmZuzTWk|o5Z&utZ`RJ!@q^fTElC7fNF$JVhDRyTq$DJvF>XOT+^v173-N>S1yF(RHW=7OiIN9vZDtV=aglN6OB zQdBug1~k6X<)1pHh21G)xM$HW;)9q&P!XKB03%+gu+v`%i4wH~l%7$~a%;*hxGiN_Ne)3)Pr zy97kKSmRFD9*w8WD6 zgu6qwlo-Do2-Y2Hjwu^u(1z7od`L;3B*mnGQn+!$*_bOP$CRk2U5-f^bxuP{Nrt!2 zkZ18U)-q2FnYrfqMb1zbH5R#Ly66D}3yzA$=!b|oxf#3ztOfY=;@YLbY{8wXNt-p5Y)B1bC_8aT(1vXsp zJgM|wJTr0T3ccZZSlMx}vSX&K>SFIi?_~c>S@ly@3f0c|>)`L#5~aa=L3yA>SQDI< zz&y?`1sJTz^S6KiLJ1@uI9xAD(h00d^V*R?o+Ef)0UY@ zpE7lt(N7d;DyNU>*#fom%+w2(o~4#;o}lfWr#y&8y?=zV0tR;L+H0n*4^x&*Cvz+H zeO8`Yc~iFvbOiL^t-TM*8vsp)Av>Bqm>W%7@8ldWqc+XQkBNHdu(uSMa1Y7xFJ`!X zzH)0(5n(VcJy}tAL76Q3NvnRS)!x@?uk?Ix^!}*z&1hv8D<>)+XkpgGAN#|+v*eGx zSX{Om34+*B5@V(XfI=KN9%>Z;6K0+|o+L9z;|ZF{e60y|2vp1&@^P(&MEK|0Dzcv6 z(F&xA@Alkp5sg@tU=$q08BOF(E0v+foPF_3zQx-L9S+BD#S;TdZnkh@2OswifX^Si z-!v>lYPcK?0i4Z2#sz9+7UEY+Yao!DrK`v*{7GpKX_{^;t0xEWl6|xPz*@X2WM5oT z$)xSPBcDnSvo=tJyCe9Epf6{+JOrPN7drt91!!jx{z>^7(#da^55o!DD%Oy;Eof7P z1RIQ1Bk2HQYd}PY2tyRN@u|u%>F3ugM^yn~neVCy@aO&9x zNS5o`>cTn2R_x_DQPY-9Sze?G zE+}%n9d>Q(Jj^X>9_K@$T5@9gz0h6K9R-Qw9!RGK^CDD7*_#`+99L}EkR5f1j@e^b zb^y)Z1TyE-anlj#vSnTy_|=+5lH=dij6k%T!yd958%B&wLlwQ~l!+dU(~|%SoAu&$ z90~e|Sqj}o8PnlqzA5>+NGloR*CR&&q1aS+;|35o!DaE`z`}Vo$*UUAt;5DI_JMFK znnCmihHc2{HV8U>2ns&ifPxkLR^z7E%h4!^L_~mtP9(0F!iD;ZZdg*7}cDwxnvO1_%N(5JB94$zT{aHc^D;dUx&0+S_sNWn+W{ zVgf;piQ9?t*F`ZV#)M?iM1M%)FT)?j#Dp9Y@o&`_7=9&2^nK5z-I{okK0W82_w#w* z^S(ZCx3B&|BodNfo&EG^?p$oHKAG7Ar+1_gX@ne-C=vfMm6KAZG$J=kBg)HUSZB}3 zv2F$RB#EjyVyI=@9(RaR;PTu;oW$lnJJn3%qtb3d6B)^f^UzV-qj`70EZSvt zaWDCt^wfba4I)GUILY#0Hft43*Ue_#0DM|2)F-!R4S+-D^^Z2LPrdxj3(&1 z3K$BNEBoc0p)N}-?`mYx0(__%@|Dq68Npy*=&^o{>(q3{OGU0*6Q#Um(|Wk$O2Kw$ zqi+Dy;<`IwPT4M94+j^C1s?n?+u82GT2f*s0|$G4Of=t)#c#)xR}an|{6Kkc|3~SC zXWx(9if_7}z7^YcKcKdUuBhMFHPbYEm`Jh7;2%k}NH-un2EbKo)^s53Mm8I2Z9w0` z=OYPrEtDZCb~ChrWZ3=C8IoqXRo`Vqu^KFDo1UGew&j^lu3#H=ROtK0*@*9jH|iw2rYS#$#~W$ zy-GLxa&oml!M^mJa-HgrvYkiHM zoT_EskVSZO2s91dk7hsGJAhCt>*`hWeiQ72GmkC5r_B}e4#R~z#HJGK*!kFI2>avM z5E-nsCR)_ceD@+F%!9A83bskJg}eiO*A8F)FEiGc=pw^xGBFRfA0~bw&>cTF?Ib7J z`ee^8VMj5YS=x>INEFxej%!m-5C%C5*0;fhi;tqx)MM<8WD7a5_+j!JGB}88+HgKM z;fYYWy00|jaCvmfP`Ey6&$_ckIsiiSIej#p!~jooPOAg{2y&C^hVzXK^& zYPr`Z9;kScI*p;L@J!x$wUo8bLZ7%4siiLE6R(l!_w=w@8#vS+%6%O{^%y7>cwZV2l*GqSFYo%&0e2N9PktYf9{I_ ze_fXTl!IDoX^pJ4EQKOkXsMlj-Fd*paU?_yjR(BRyyMxF%ibjIf(x$qo6fZ93ITc? zMTI;LHD32ru{3_BVDF)q;0Sq2E=G7yA%xtK;&-ITZ)(3xTHbEFnwm@fF5zCN{|Cgq Bnw$Ut diff --git a/src/services/__pycache__/threads_manager.cpython-313.pyc b/src/services/__pycache__/threads_manager.cpython-313.pyc index 103a7ae1060a79b70f9041ba14c5d8272d1729c8..104089353ba58ce6bb668d4eb1f822e77bbaf22b 100644 GIT binary patch delta 4205 zcmcInZ){uD6~FKKJ^%NM6Wg(4JIQmLrg4grrb+19LQ9&4KP{x8PNYI8#yBsjTgMLf zInb8TRA^Lm0?~4ZsMHN@Op`!DT2Q2E(ijrZ51TZhdd=qSQM7*9hwj4`(lR>G{+x54 zowyla_nIExd*8X|-gEBn{LX!^j=rB#ZhAaU0#DbcXQv;ITu?&7*oDpNG4X(9zeFXK zBuUb8K;CbqR^FBl*!J70T_qvXMv`)vB&`D?&L@OAriFy<1~x|$g5f%pp3D|TbLo>B zg|0oBq3PLK-gb{2rCNGY%a5h?;}=#Mg(+qeQuAR#>yuvEFkPQ$Fcecqe9i1i# zaU@|gq+)hPGh9V&211`M&QZ;IIRo7?Q5#rf36>;9MPbRaG?(46!|y znV=iTo(6G-EJr$)?4jjo=aRjN{o2yj64`ud_qA}xrS5CNI6HkU+IeZ@i#mIw^JVE3 z=SsukZA+ctz*R>QGW9dB>~F=MARt-K6byeS#YuYJYl7JSSlRnAvas_MA$)xRdBPI z*C!}k)`McRd>0{kfn9Ng*e@Ls!Oz~8TAYt`6XGH&nFzAO&K5B#v#4{6O~xG_vVewf z6FC%y&W2}w&1vhjHDP0Co&9ZyZi?qYJt*+BXdQ@=ghKt;X=Y_-Ux(qF;Tf2!MGFrB z?L@5viU5na;z9%4;|e$6InXW;>*sLXwbcsl5dB~5C$1;r%Xar`&U4Q5yG!=Avc0op z?|jE!P7Ib3gI5znOZI)&U3E-!zZgYNGHbaD9n1xu+|6ZoPs!c$2Y2s^g}66Sj+@2j>pY@R$IjLFc=-wNjqqHmW520y z?ZYE8WId;8vxZ~%L`Iv%J<%O7{B9)y1Z^$s=tVy}(%S344gUEr6Fq;WCHy~baAYKyYDbpzXVYqoK2GEexQ($G?x2iLT;U!Ii z;LbdC-+i#N8{u|gnkkX@%yeE{io7jAfu1(mlU+riOo7E2f|X=2A7pY_jGN+|XLgYc?$N1r6Ka=tHRk!;cx#&_iR7@pIr= ztRo3L1yl6JAa%YCwyr8$Z{KnffAdWudRnh}16M-53$X>?mBxW3@7-&Y-&8Ir->k@> z^0gjAu&@s!jjq!G`X|Yg;u~a)4cem&kYK$H zNPzA$_CPHbIm%3UU2yOlj1lT7yHGeB}|Ul zs~1j{9K5RrFw#0yYA3hjoh?LT?2KeZXXe4bN4^52 z+NN!QR3s7p08$}1cOcce5vc%5TLqN-w+57sQ#%GT#kq7Ydn&Ew(rPB1FJ>pxlj@|V zX5en66?8RUP;**Sb`qL^*J2j#ZUq(D!mQNn&+#~fJitC}>V1soNYb)HXeS7GEaj&f zhAM{iY#uCWbf-!;5|`ng%i|p!fIgQmuCX#q=gew!b0S}84_SW%V|p(;(-O!ZE~)pI z)zOkVx*Q)^$Spj1CAJUhSL{*Y3nIqC6bX2)1H?!{J(@lV4&}8I*-sYpYC5Ot8hsXg zqU)Y!Ky&TEOC(mKn@In=1rt{2mLO1*S$!6+rBCOybr)$nnBW(@+FEfM z27iVxy3m*T8ed{NHac;txt1=DW^=I6+WG8MM{WRENy2U3lXxLTCZi`%oCIMsOlif; z(Nt}WYv>(_cT0uj*NSbmAYoypFMu!$64E(4xT$0QbsYK!h<~0T*DG`pDk*Jcr3?O6 z#DHhlx6#G7u*G#>uLK;tci1j1Lsdzbt}wR)!7t#vgo@zlSz|NHyT(^p1T zdaC5KzVoX}*X>(foSX#DE>2DZ3+aXAmBt-q@6M8U=Qk@Bc=7PpdJ8!J_Fmt98&^X%dG~6gRSvIuJ#uul#V&_d{dT$aOMjCb z;pxOpC!F+gT!w=s53PBlNvD}^o>Cc2@8+M{Df3&Kf1VkEJKpP+Z*PiOW`yr}SFC)C zr|>Roe(Z3c&f$t{ej4_BW(t#YIc+z6m5^-&uUUE=1e}f_d`9X%BQB7CA-$iIuFpx| OSJt#BL{zbIpE;h{e&=ok!tR{ zD2{5>^i5o1dVWT)OyrGu_*Sx4jk;bwTdkDp)jexd5s?Kh_9ih`VewSNf!D2uQntn^KaW-Gc7j<^m2lh>L!w9huf}Q`v)|Ze`3<792(_!cfvn&iB1_m{eFf#@IpuHUQW7DGm%VaY)@PSL^ zckIco=)le4k2?GLQy;}sH%C8FoDt7;$Mc@;z$$gL(W7W&RFhZ-yiF%ttW-*sqA7u2 ztS&q-9W_X~W@DW=!oJ8Bnr4~Gza=pmfqNrd-<3<&M$ewe8uIFi&9a7#QoJX(yhQ)hEZmiZ599+)f5pr z*25q49tsNgSqghKgb4q!x1Waj&%MzQ#*YmG+>hVK-UBlDLX1E2xbNH2woH5he$)3f zEVW1JqT_tJJ;pbbEdPn}OB&+3e;2>!FAX`ZY)K_3U4!ic1*TMErAl2y((<7jS}~^l zR+~yy{$AU#d;%?fpZ~q>S%RHQT|S}8-o9qmgMhtP4aXsAZJj| zSw0h`9AF+Me_l=}H<&zt?0H)z5tNI}P)g`loS_*1fo<*)R5Xzh((Cv5a0zmrV41Q}$Y;U~#3O>n8dR~AzWe6T-7zss}zHEWUn zAqd5y>-0p0}hty1Q~aU;{R+< zIFO~2bdrqNvW_U3gL9B?1T`x0wNzhIU!vm)LW=OG$)gN+P*Kfy`A@ZeNczOr#pjcu zlU2s_s*x~Cl>|_neKo$|sBuw23%zrA`75}F$q6Amc z-%J01JwLjwkhbub^u6yN6l>9B7U~7d;t;cTC{gf&lDLXdEyVw}@1a9oIH(U{H^N?o z5riWMhY$`TU`z$^Sb^t;lS>#di;Z{%UIS#(-Bk(1^RGNJDi>#~Kk?iZE^bVM7w(s=T?{A9?i_8q}bQ#T?7{j}O z*|F0uZe_Vk*1gQSIEV;51H&w@vEWSC(QLeeZ9Ap(L(=vk@d{M_ cLiTTwgIi?p78%$g`~K-TW23PKK@{5l1875Sxc~qF diff --git a/src/services/email_client_pop.py b/src/services/email_client_pop.py index 5c6ce9d..11cc23d 100644 --- a/src/services/email_client_pop.py +++ b/src/services/email_client_pop.py @@ -8,10 +8,11 @@ from email.mime.multipart import MIMEMultipart import sqlite3 from datetime import datetime + class EmailClientPOP: - def __init__(self, pop_server, smtp_server, email, password, pop_port = 110, smtp_port=25): + def __init__(self, pop_server, smtp_server, email, password, pop_port=110, smtp_port=25): self.pop_server = pop_server - self.smtp = smtp_server + self.smtp_server = smtp_server self.email = email self.password = password self.pop_port = pop_port @@ -19,7 +20,7 @@ class EmailClientPOP: self.pop_conn = None self.smtp_conn = None - #Ruta del archivo SQLite: + # Ruta del archivo SQLite: self.db_file = os.path.join("resources/db_email", "emails.db") self.init_database() @@ -40,8 +41,169 @@ class EmailClientPOP: received_at DATETIME ) """) + cursor.execute(""" + CREATE TABLE IF NOT EXISTS sent_emails ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + recipient TEXT NOT NULL, + subject TEXT, + body TEXT, + sent_at DATETIME + ) + """) conn.commit() conn.close() print(f"Base de datos inicializada: {self.db_file}") except sqlite3.Error as e: - print(f"Error al inicializar la base de datos: {e}") \ No newline at end of file + print(f"Error al inicializar la base de datos: {e}") + + def connect_pop(self): + """Conexión al servidor POP""" + try: + self.pop_conn = poplib.POP3(self.pop_server, self.pop_port) + self.pop_conn.user(self.email) + self.pop_conn.pass_(self.password) + print("Conexión POP exitosa") + except Exception as e: + print(f"Error al conectar al servidor POP: {e}") + self.pop_conn = None + + def connect_smtp(self): + """Conexión al servidor SMTP""" + try: + self.smtp_conn = smtplib.SMTP(self.smtp_server, self.smtp_port) + self.smtp_conn.login(self.email, self.password) + print("Conexión SMTP exitosa") + except Exception as e: + print(f"Error al conectar al servidor SMTP: {e}") + self.smtp_conn = None + + def is_connected(self): + """Verifica si hay una conexión válida tanto de POP como de SMTP""" + return self.pop_conn is not None and self.smtp_conn is not None + + def reconnect(self): + """Intenta reconectar a los servidores POP y SMTP""" + print("Intentando reconectar al servidor de correo...") + self.connect_pop() + self.connect_smtp() + + def fetch_unread_count(self): + """Obtener el número de correos (POP3 no distingue entre leídos y no leídos)""" + try: + if not self.pop_conn: + self.connect_pop() + num_messages = len(self.pop_conn.list()[1]) + return num_messages # Total de mensajes + except Exception as e: + print(f"Error al obtener el conteo de correos: {e}") + return 0 + + def fetch_emails(self, save_to_db=True): + """Obtiene correos desde el servidor POP""" + try: + if not self.pop_conn: + self.connect_pop() + + emails = [] + num_messages = len(self.pop_conn.list()[1]) + for i in range(1, num_messages + 1): + raw_messages = b"\n".join(self.pop_conn.retr(i)[1]) + msg = email.message_from_bytes(raw_messages) + subject, encoding = decode_header(msg["Subject"])[0] + if isinstance(subject, bytes): + subject = subject.decode(encoding or "utf-8") + sender = msg.get("From") + body = "" + if msg.is_multipart(): + for part in msg.walk(): + if part.get_content_type() == "text/plain": + body = part.get_payload(decode=True).decode("utf-8", errors="ignore") + else: + body = msg.get_payload(decode=True).decode("utf-8", errors="ignore") + + email_data = { + "sender": sender, + "subject": subject, + "body": body, + "received_at": datetime.now().isoformat() + } + emails.append(email_data) + + if save_to_db: + self.save_email_to_db(email_data) + + return emails + except Exception as e: + print(f"Error al obtener los correos: {e}") + return [] + + def fetch_folders(self): + """POP3 no tiene carpetas, devuelve un mensaje indicando esto""" + print("El protocolo POP3 no soporta carpetas. Devuelve solo la bandeja de entrada.") + return ["INBOX"] + + def save_email_to_db(self, email_data): + """Guarda un correo recibido en la base de datos.""" + try: + conn = sqlite3.connect(self.db_file) + cursor = conn.cursor() + cursor.execute(""" + INSERT INTO emails (sender, subject, body, received_at) + VALUES (?, ?, ?, ?) + """, (email_data["sender"], email_data["subject"], email_data["body"], email_data["received_at"])) + conn.commit() + conn.close() + print("Correo recibido guardado en la base de datos.") + except sqlite3.Error as e: + print(f"Error al guardar correo recibido: {e}") + + def send_mail(self, recipient, subject, body, save_to_db=True): + """Envía un correo usando SMTP""" + try: + if not self.smtp_conn: + self.connect_smtp() + + msg = MIMEMultipart() + msg["From"] = self.email + msg["To"] = recipient + msg["Subject"] = subject + msg.attach(MIMEText(body, "plain")) + self.smtp_conn.sendmail(self.email, recipient, msg.as_string()) + print(f"Correo ha sido enviado a {recipient}") + + if save_to_db: + self.save_sent_mail_to_db({ + "recipient": recipient, + "subject": subject, + "body": body, + "sent_at": datetime.now().isoformat() + }) + except Exception as e: + print(f"Error al enviar correo: {e}") + + def save_sent_mail_to_db(self, email_data): + """Guarda un correo enviado en la base de datos.""" + try: + conn = sqlite3.connect(self.db_file) + cursor = conn.cursor() + cursor.execute(""" + INSERT INTO sent_emails (recipient, subject, body, sent_at) + VALUES (?, ?, ?, ?) + """, (email_data["recipient"], email_data["subject"], email_data["body"], email_data["sent_at"])) + conn.commit() + conn.close() + print("Correo enviado guardado en la base de datos.") + except sqlite3.Error as e: + print(f"Error al guardar correo enviado: {e}") + + def list_emails(self, limit=10): + """Lista correos recientes (máximo `limit`)""" + emails = self.fetch_emails() + return emails[:limit] + + def close_connections(self): + """Cierra las conexiones POP y SMTP""" + if self.pop_conn: + self.pop_conn.quit() + if self.smtp_conn: + self.smtp_conn.quit() diff --git a/src/ui/__pycache__/centered_window.cpython-313.pyc b/src/ui/__pycache__/centered_window.cpython-313.pyc index 20a2c4cc1f3fcd2bba89ac4c471a6af40bee5b62..ed43031ecbbfe7a41a368cf2dbb637eb16f1ea5d 100644 GIT binary patch delta 8670 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 diff --git a/src/ui/centered_window.py b/src/ui/centered_window.py index b893300..555ff3d 100644 --- a/src/ui/centered_window.py +++ b/src/ui/centered_window.py @@ -6,7 +6,7 @@ from src.services.processes_manager import ProcessManager from src.services.system_monitor import SystemMonitor from src.services.tetris_game import TetrisGame from src.services.threads_manager import ThreadsManager -from src.services.email_client_imap import EmailClientImap +from src.services.email_client_pop import EmailClientPOP class CenteredWindow(ctk.CTk): @@ -16,9 +16,9 @@ class CenteredWindow(ctk.CTk): self.title(title) self.after_tasks = [] - #Configurar Email Client - self.email_client = EmailClientImap( - imap_server="192.168.120.103", + # Configurar Email Client IMAP + self.email_client = EmailClientPOP( + pop_server="192.168.120.103", smtp_server="192.168.120.103", email="dennis@psp.ieslamar.org", password="1234" @@ -28,21 +28,21 @@ class CenteredWindow(ctk.CTk): self.process_manager = ProcessManager() self.system_monitor = None - # Obtener la resolucion de la pantalla: + # Obtener la resolución de la pantalla: screen_width = self.winfo_screenwidth() screen_height = self.winfo_screenheight() - # Calcula el tamaño de la ventana según procentaje de la pantalla: + # Calcula el tamaño de la ventana según porcentaje de la pantalla: window_width = int(screen_width * width_percentage) window_height = int(screen_height * height_percentage) - # Calcular la posicion para centrar la ventana: + # Calcular la posición para centrar la ventana: position_x = (screen_width - window_width) // 2 position_y = (screen_height - window_height) // 2 self.geometry(f"{window_width}x{window_height}+{position_x}+{position_y}") - #Configura la ventana + # Configura la ventana self.configure_window() self.protocol("WM_DELETE_WINDOW", self.on_close) @@ -62,7 +62,6 @@ class CenteredWindow(ctk.CTk): self.thread_manager.start_threads() - def on_close(self): """Maneja el cierre de la ventana""" self.thread_manager.stop_threads() @@ -70,7 +69,7 @@ class CenteredWindow(ctk.CTk): if hasattr(self, "tetris_game") and self.tetris_game.running: self.tetris_game.stop_game() - if "tetris_game" in self.thread_manager.tasks: + if "tetris_game" in self.thread_manager.tasks: self.thread_manager.tasks["tetris_game"].stop() if hasattr(self.thread_manager, "scrapper"): @@ -91,10 +90,16 @@ class CenteredWindow(ctk.CTk): # Secciones y botones sections = { "Aplicaciones": [ - ("Abrir Chrome", lambda: self.process_manager.open_resource("browser", "https://google.com", "Cannot open browser")), - ("Visual Studio Code", lambda: self.process_manager.open_resource("program", r"C:\Program Files\Microsoft VS Code\Code.exe", "Can't find VSCode")), - ("Explorador de Windows", lambda: self.process_manager.open_resource("program", "explorer.exe", "Can't open Windows Explorer")), - ("Notepad++", lambda: self.process_manager.open_resource("program", r"C:\Program Files\Notepad++\notepad++.exe", "Can't open Notepad++")) + ("Abrir Chrome", + lambda: self.process_manager.open_resource("browser", "https://google.com", "Cannot open browser")), + ("Visual Studio Code", + lambda: self.process_manager.open_resource("program", r"C:\Program Files\Microsoft VS Code\Code.exe", + "Can't find VSCode")), + ("Explorador de Windows", + lambda: self.process_manager.open_resource("program", "explorer.exe", "Can't open Windows Explorer")), + ("Notepad++", + lambda: self.process_manager.open_resource("program", r"C:\Program Files\Notepad++\notepad++.exe", + "Can't open Notepad++")) ] } @@ -104,13 +109,13 @@ class CenteredWindow(ctk.CTk): url_entry_chrome.pack(pady=5, padx=10) # Botón para abrir la URL ingresada - internet_access_button = ctk.CTkButton( - left_panel, - text="Buscar URL", - command=lambda: self.process_manager.open_resource("browser", url_entry_chrome.get(), "Cannot open browser") - ) + internet_access_button = ctk.CTkButton( + left_panel, + text="Buscar URL", + command=lambda: self.process_manager.open_resource("browser", url_entry_chrome.get(), "Cannot open browser") + ) internet_access_button.pack(pady=5, padx=10) - + for section, buttons in sections.items(): if section: section_label = ctk.CTkLabel(left_panel, text=section, font=("Arial", 12, "bold")) @@ -122,20 +127,20 @@ class CenteredWindow(ctk.CTk): scrapping_label = ctk.CTkLabel(left_panel, text="Scrapping", font=("Arial", 12, "bold")) scrapping_label.pack(anchor=ctk.W, pady=5, padx=10) - url_entry = ctk.CTkEntry(left_panel, placeholder_text="Introduce la URL para scrapear") - url_entry.pack(pady=5, padx=10) + url_entry = ctk.CTkEntry(left_panel, placeholder_text="Introduce la URL para scrapear") + url_entry.pack(pady=5, padx=10) self.left_panel = left_panel self.left_panel.url_entry = url_entry self.left_panel.url_entry_chrome = url_entry_chrome - start_button = ctk.CTkButton(left_panel, text="Iniciar Scrapping", command=self.thread_manager.scrapper.start_scraping) + start_button = ctk.CTkButton(left_panel, text="Iniciar Scrapping", + command=self.thread_manager.scrapper.start_scraping) start_button.pack(pady=5, padx=10) - stop_button = ctk.CTkButton(left_panel, text="Detener Scrapping", command=self.thread_manager.scrapper.stop_scraping) + stop_button = ctk.CTkButton(left_panel, text="Detener Scrapping", + command=self.thread_manager.scrapper.stop_scraping) stop_button.pack(pady=5, padx=10) - - def create_center_panel(self): # Panel central con pestañas center_panel = ctk.CTkFrame(self) @@ -147,9 +152,12 @@ class CenteredWindow(ctk.CTk): # Crear pestañas y manejar contenido por separado for tab_name in ["Scrapping", "Radio", "Correos", "Juego", "Sistema"]: tab = tab_view.add(tab_name) - - if tab_name == "Radio": - self.create_radio_tab(tab) + + if tab_name == "Radio": + self.create_radio_tab(tab) + + if tab_name == "Correos": + self.create_email_tab(tab) if tab_name == "Scrapping": text_widget = ctk.CTkTextbox(tab, width=500, height=400) @@ -161,11 +169,11 @@ class CenteredWindow(ctk.CTk): if tab_name == "Sistema": # Crear un frame para los gráficos del sistema - system_frame = ctk.CTkFrame(tab) - system_frame.pack(fill=ctk.BOTH, expand=True, padx=5, pady=5) + system_frame = ctk.CTkFrame(tab) + system_frame.pack(fill=ctk.BOTH, expand=True, padx=5, pady=5) # Inicializar SystemMonitor con el frame de la pestaña - self.system_monitor = SystemMonitor(system_frame) + self.system_monitor = SystemMonitor(system_frame) # Asignar el system_monitor al thread_manager self.thread_manager.set_system_monitor(self.system_monitor) @@ -192,33 +200,25 @@ class CenteredWindow(ctk.CTk): self.tetris_game = TetrisGame(game_frame) self.tetris_game.pack() - # else: - # Agregar contenido genérico a otras pestañas - #label = ctk.CTkLabel(tab, text=f"Contenido de {tab_name}", font=("Arial", 12)) - #label.pack(pady=10) - - + # else: + # Agregar contenido genérico a otras pestañas + # label = ctk.CTkLabel(tab, text=f"Contenido de {tab_name}", font=("Arial", 12)) + # label.pack(pady=10) def start_tetris_game(self): """Método para iniciar el juego.""" if not self.tetris_game.running: self.tetris_game.running = True - #self.tetris_game.update_game() - - + # self.tetris_game.update_game() def pause_tetris_game(self): """Método para pausar el juego.""" self.tetris_game.running = False - - def restart_tetris_game(self): """Método para reiniciar el juego.""" self.tetris_game.reset_game() - - def create_right_panel(self): # Panel derecho right_panel = ctk.CTkFrame(self, width=250) @@ -236,7 +236,8 @@ class CenteredWindow(ctk.CTk): # Lista de alumnos for i in range(1, 4): - student_label = ctk.CTkLabel(right_panel, text=f"Alumno {i}", font=("Arial", 12, "bold"), text_color="black") + student_label = ctk.CTkLabel(right_panel, text=f"Alumno {i}", font=("Arial", 12, "bold"), + text_color="black") student_label.pack(anchor=ctk.W, pady=5, padx=10) student_info = ctk.CTkLabel( @@ -247,8 +248,6 @@ class CenteredWindow(ctk.CTk): ) student_info.pack(anchor=ctk.W, padx=10) - - def create_bottom_bar(self): # Crear la barra inferior self.bottom_bar = ctk.CTkFrame(self, fg_color="lightblue", height=40) @@ -258,7 +257,8 @@ class CenteredWindow(ctk.CTk): self.info_labels = { "hora": ctk.CTkLabel(self.bottom_bar, text="Hora: --:--:--", font=("Arial", 12), text_color="black"), "fecha": ctk.CTkLabel(self.bottom_bar, text="Fecha: --/--/----", font=("Arial", 12), text_color="black"), - "temperatura": ctk.CTkLabel(self.bottom_bar, text="Temperatura local: --°C", font=("Arial", 12), text_color="black"), + "temperatura": ctk.CTkLabel(self.bottom_bar, text="Temperatura local: --°C", font=("Arial", 12), + text_color="black"), "emails": ctk.CTkLabel(self.bottom_bar, text="Correos sin leer: 0", font=("Arial", 12), text_color="black"), } @@ -266,58 +266,141 @@ class CenteredWindow(ctk.CTk): for label in self.info_labels.values(): label.pack(side=ctk.LEFT, padx=10, pady=5) - - def dummy_action(self): print("Acción no implementada") - - def create_radio_tab(self, tab): - """Crea la interfaz para la funcionalidad de emisoras de radio.""" - self.radio_player = self.thread_manager.radio_player + + def create_radio_tab(self, tab): + """Crea la interfaz para la funcionalidad de emisoras de radio.""" + self.radio_player = self.thread_manager.radio_player # Lista de emisoras - radio_stations = { - "Box Radio UK": "http://uk2.internet-radio.com:8024/", - "Jazz Radio": "http://us2.internet-radio.com:8443/", - "Deep House Radio": "http://uk7.internet-radio.com:8000/", - } + radio_stations = { + "Box Radio UK": "http://uk2.internet-radio.com:8024/", + "Jazz Radio": "http://us2.internet-radio.com:8443/", + "Deep House Radio": "http://uk7.internet-radio.com:8000/", + } # Dropdown para seleccionar emisora - self.selected_station = ctk.StringVar(value="Selecciona una emisora") - station_menu = ctk.CTkOptionMenu(tab, variable=self.selected_station, values=list(radio_stations.keys())) - station_menu.pack(pady=10) + self.selected_station = ctk.StringVar(value="Selecciona una emisora") + station_menu = ctk.CTkOptionMenu(tab, variable=self.selected_station, values=list(radio_stations.keys())) + station_menu.pack(pady=10) # Botón para reproducir - play_button = ctk.CTkButton( - tab, - text="Reproducir", - command=lambda: self.start_radio(radio_stations[self.selected_station.get()]) - ) - play_button.pack(pady=5) + play_button = ctk.CTkButton( + tab, + text="Reproducir", + command=lambda: self.start_radio(radio_stations[self.selected_station.get()]) + ) + play_button.pack(pady=5) # Botón para detener - stop_button = ctk.CTkButton( - tab, - text="Detener", - command=self.stop_radio, + stop_button = ctk.CTkButton( + tab, + text="Detener", + command=self.stop_radio, state=tk.DISABLED # Deshabilitado inicialmente - ) - stop_button.pack(pady=5) + ) + stop_button.pack(pady=5) # Guardar referencias para habilitar/deshabilitar botones - self.radio_controls = {"play_button": play_button, "stop_button": stop_button} + self.radio_controls = {"play_button": play_button, "stop_button": stop_button} - def start_radio(self, url): - """Inicia la reproducción de radio y actualiza los botones.""" - if url == "Selecciona una emisora": - tk.messagebox.showwarning("Advertencia", "Por favor, selecciona una emisora válida.") - return - self.radio_player.play(url) - self.radio_controls["play_button"].configure(state=tk.DISABLED) - self.radio_controls["stop_button"].configure(state=tk.NORMAL) + def start_radio(self, url): + """Inicia la reproducción de radio y actualiza los botones.""" + if url == "Selecciona una emisora": + tk.messagebox.showwarning("Advertencia", "Por favor, selecciona una emisora válida.") + return + self.radio_player.play(url) + self.radio_controls["play_button"].configure(state=tk.DISABLED) + self.radio_controls["stop_button"].configure(state=tk.NORMAL) - def stop_radio(self): - """Detiene la reproducción de radio y actualiza los botones.""" - self.radio_player.stop() - self.radio_controls["play_button"].configure(state=tk.NORMAL) - self.radio_controls["stop_button"].configure(state=tk.DISABLED) \ No newline at end of file + def stop_radio(self): + """Detiene la reproducción de radio y actualiza los botones.""" + self.radio_player.stop() + self.radio_controls["play_button"].configure(state=tk.NORMAL) + self.radio_controls["stop_button"].configure(state=tk.DISABLED) + + def create_email_tab(self, tab): + """Crea una interfaz moderna para gestionar los correos con customtkinter.""" + # Configurar el grid para permitir que los elementos se expandan + tab.grid_columnconfigure(0, weight=1) + tab.grid_rowconfigure(0, weight=1) + + # Crear un marco principal para la pestaña + main_frame = ctk.CTkFrame(tab) + main_frame.grid(row=0, column=0, sticky="nsew", padx=10, pady=10) + + # Lista de correos en un marco + listbox_frame = ctk.CTkFrame(main_frame) + listbox_frame.grid(row=0, column=0, sticky="nsew", padx=10, pady=10) + + # Crear una lista de correos con un scrollbar + self.email_listbox = ctk.CTkTextbox(listbox_frame, width=800, height=800) + self.email_listbox.grid(row=0, column=0, sticky="nsew", padx=10, pady=10) + self.email_listbox.configure(state="disabled") # Inicialmente deshabilitada + + scrollbar = ctk.CTkScrollbar(listbox_frame, command=self.email_listbox.yview) + scrollbar.grid(row=0, column=1, sticky="ns") + self.email_listbox.configure(yscrollcommand=scrollbar.set) + + # Frame para botones de acción + button_frame = ctk.CTkFrame(main_frame) + button_frame.grid(row=1, column=0, sticky="ew", padx=10, pady=10) + + # Crear botones para acciones: Marcar como leído, Eliminar, Actualizar + mark_read_button = ctk.CTkButton( + button_frame, text="Marcar como leído", command=self.mark_email_as_read, fg_color="green" + ) + mark_read_button.grid(row=0, column=0, padx=5, pady=5, sticky="ew") + + delete_button = ctk.CTkButton( + button_frame, text="Eliminar correo", command=self.delete_email, fg_color="red" + ) + delete_button.grid(row=0, column=1, padx=5, pady=5, sticky="ew") + + refresh_button = ctk.CTkButton( + button_frame, text="Actualizar", command=self.refresh_email_list, fg_color="blue" + ) + refresh_button.grid(row=0, column=2, padx=5, pady=5, sticky="ew") + + # Expandir las filas y columnas del marco principal + main_frame.grid_rowconfigure(0, weight=1) + main_frame.grid_columnconfigure(0, weight=1) + + def refresh_email_list(self): + """Actualiza la lista de correos en la interfaz.""" + try: + if not self.email_client.is_connected(): + self.email_client.reconnect() + + if self.email_client.is_connected(): + emails = self.email_client.fetch_emails() + self.email_listbox.delete(0, tk.END) + for email in emails: + self.email_listbox.insert(tk.END, f"{email['subject']} - {email['from']}") + else: + print("No hay conexión al servidor de correo.") + except Exception as e: + print(f"Error al actualizar la lista de correos: {e}") + + def mark_email_as_read(self): + """Marca el correo seleccionado como leído.""" + selected_index = self.email_listbox.curselection() + if not selected_index: + print("No se ha seleccionado ningún correo.") + return + + selected_email = self.email_listbox.get(selected_index) + # Aquí puedes agregar la lógica para marcar el correo como leído + print(f"Correo marcado como leído: {selected_email}") + + def delete_email(self): + """Elimina el correo seleccionado.""" + selected_index = self.email_listbox.curselection() + if not selected_index: + print("No se ha seleccionado ningún correo.") + return + + selected_email = self.email_listbox.get(selected_index) + # Aquí puedes agregar la lógica para eliminar el correo + print(f"Correo eliminado: {selected_email}")