
    sW[ip8                         S SK r S SKrS SKrS SKrS SKJrJr  S SKJrJrJ	r	J
r
  S SKrS SKJr   " S S5      r " S S5      rg)	    N)datetime	timedelta)DictListOptionalSet)EmailCheckerc                       \ rS rSrSrSS\4S jjrS rS rS\S\	4S	 jr
SS\S\\   4S jjrSS\4S jjrS\4S jrSrg
)EmailTracker   z4Track processed emails to avoid duplicate processingtracking_filec                     Xl         0 U l        [        R                  " 5       U l        [
        R                  " [        5      U l        U R                  5         g)zf
Initialize email tracker

Args:
    tracking_file: Path to JSON file for storing processed email IDs
N)
r   processed_emails	threadingLocklocklogging	getLogger__name__logger_load_tracking_data)selfr   s     "/var/www/html/troy/email_poller.py__init__EmailTracker.__init__   s>     +13NN$	''1  "    c                 d   [         R                  R                  U R                  5      (       a   [	        U R                  S5       n[
        R                  " U5      nUR                  S0 5      U l        SSS5        U R                  R                  S[        U R                  5       SU R                   35        gU R                  R                  SU R                   S35        g! , (       d  f       Nw= f! [         a/  nU R                  R                  SU 35        0 U l         SnAgSnAff = f)	z"Load processed email IDs from filerr   NzLoaded z processed email IDs from zError loading tracking data: zTracking file z  does not exist. Starting fresh.)ospathexistsr   openjsonloadgetr   r   infolen	Exceptionerror)r   fdataes       r   r    EmailTracker._load_tracking_data   s    77>>$,,--+$,,c2a99Q<D,0HH5G,LD) 3   73t/D/D+E*FF`aeasas`t!uv
 KK~d.@.@-AAabc 32  +!!$A!"EF(*%%+s0   C6 .C%4AC6 %
C3/C6 6
D/ %D**D/c                 X    [        U R                  S5       n[        R                  " U R                  [
        R                  " 5       R                  5       S.USS9  SSS5        g! , (       d  f       g= f! [         a(  nU R                  R                  SU 35         SnAgSnAff = f)z Save processed email IDs to filew)r   last_updated   indentNzError saving tracking data: )r"   r   r#   dumpr   r   now	isoformatr(   r   r)   r   r*   r,   s      r   _save_tracking_data EmailTracker._save_tracking_data)   s    	Bd((#.!		(,(=(=$,LLN$<$<$> Q  /..
  	BKK <QC@AA	Bs5   A7 AA&A7 &
A40A7 4A7 7
B)B$$B)email_idreturnc                 l    U R                      XR                  ;   sSSS5        $ ! , (       d  f       g= f)z,Check if an email has already been processedN)r   r   )r   r:   s     r   is_email_processedEmailTracker.is_email_processed4   s    YY444 YYs   %
3N
email_datac                 &   U R                      [        R                  " 5       R                  5       U=(       d    0 S.U R                  U'   U R                  5         U R                  R                  SU S35        SSS5        g! , (       d  f       g= f)z
Mark an email as processed

Args:
    email_id: Unique email ID from Microsoft Graph
    email_data: Optional email data to store
)	timestampr?   zMarked email z as processedN)r   r   r5   r6   r   r8   r   debug)r   r:   r?   s      r   mark_email_processed!EmailTracker.mark_email_processed9   sj     YY%\\^557(.B/D!!(+ $$&KKhZ}EF YYs   A,B
Bdaysc           	         [         R                  " 5       [        US9-
  nU R                     [	        U R
                  5      nU R
                  R                  5        VVs0 sH&  u  pE[         R                  " US   5      U:  d  M$  XE_M(     snnU l        U[	        U R
                  5      -
  nUS:  a2  U R                  5         U R                  R                  SU SU S35        SSS5        gs  snnf ! , (       d  f       g= f)zq
Remove tracking records older than specified days

Args:
    days: Number of days to keep records (default: 30)
)rE   rA   r   zCleaned up z" old tracking records (older than z days)N)r   r5   r   r   r'   r   itemsfromisoformatr8   r   r&   )r   rE   cutoff_dateinitial_countr:   r+   removeds          r   cleanup_old_records EmailTracker.cleanup_old_recordsI   s     llnyd';;YY 5 56M595J5J5P5P5R%5R>8))${*;<{J 5R%D! $c$*?*?&@@G{((*  ;wi7YZ^Y__e!fg Y% Ys$   3C3"C-C-AC3-C33
Dc                 z    U R                      [        U R                  5      sSSS5        $ ! , (       d  f       g= f)z$Get total number of processed emailsN)r   r'   r   r   s    r   get_processed_count EmailTracker.get_processed_count\   s!    YYt,,- YYs   ,
:)r   r   r   r   )processed_emails.json)N)   )r   
__module____qualname____firstlineno____doc__strr   r   r8   boolr=   r   r   rC   intrL   rP   __static_attributes__ r   r   r   r      sd    >#c #d	B53 54 5
GS Ghtn G h h&.S .r   r   c                       \ rS rSrSr   SS\S\S\S\S\S	\\   4S
 jjr	S r
S rS rSS\4S jjrS\4S jrS rS rS\4S jrS\4S jrS\4S jrS\4S jrSrg)EmailPollerb   z,Background service that polls for new emailsNemail_checkersender_emailfolderintervalr   callback_urlc                    Xl         X l        X0l        X@l        X`l        [        U5      U l        [        R                  " [        5      U l
        SU l        SU l        [        R                  " 5       U l        [        R                   " 5       U l        SU l        SU l        SU l        SU l        SU l        U R/                  5         g)ag  
Initialize email poller

Args:
    email_checker: EmailChecker instance for fetching emails
    sender_email: Email address to filter by
    folder: Email folder to check (default: inbox)
    interval: Polling interval in seconds (default: 60)
    tracking_file: Path to email tracking file
    callback_url: Optional URL to call when new email is processed
FNr   zpoller_state.json)r`   ra   rb   rc   rd   r   trackerr   r   r   r   _running_threadr   Event_stop_eventr   _locklast_check_timelast_check_timestampemails_processed_count
last_error
state_file_load_state)r   r`   ra   rb   rc   r   rd   s          r   r   EmailPoller.__init__e   s     +( (#M2''137$??,^^%
3737!&'#)- .r   c                    [         R                  R                  U R                  5      (       a   [	        U R                  S5       n[
        R                  " U5      nUR                  S5      U l        U R                  (       a&   [        R                  " U R                  5      U l        UR                  SS5      U l        SSS5        U R                  R                  SU R                   SU R                   35        gg!    NZ= f! , (       d  f       NL= f! [         a(  nU R                  R!                  SU 35         SnAgSnAff = f)	zLoad polling state from filer   rm   rn   r   Nz!Loaded polling state: last_check=z, processed=zError loading state: )r   r    r!   rp   r"   r#   r$   r%   rm   r   rH   rl   rn   r   r&   r(   r)   )r   r*   stater,   s       r   rq   EmailPoller._load_state   s   77>>$//**?$//3/1 IIaLE05		:P0QD-00!3;3I3I$JcJc3dD0 38))<TVW2XD/ 0   #DTE^E^D__klp  mH  mH  lI  "J  K +!  0/  ?!!$9!"=>>?sG   D >D%D *D=D  DD
DD 
E
"EE
c                 n    [        U R                  S5       n[        R                  " U R                  U R
                  [        R                  " 5       R                  5       S.USS9  SSS5        g! , (       d  f       g= f! [         a(  nU R                  R                  SU 35         SnAgSnAff = f)zSave polling state to filer/   )rm   rn   r0   r1   r2   NzError saving state: )r"   rp   r#   r4   rm   rn   r   r5   r6   r(   r   r)   r7   s      r   _save_stateEmailPoller._save_state   s    	:doos+q		,0,E,E.2.I.I$,LLN$<$<$> Q	  ,++  	:KK 4QC899	:s5   B AA1(B 1
A?;B ?B 
B4B//B4c                    U R                      U R                  (       a%  U R                  R                  S5         SSS5        gSU l        U R                  R                  5         [        R                  " U R                  SS9U l	        U R                  R                  5         U R                  R                  SU R                   S35         SSS5        g! , (       d  f       g= f)z0Start the polling service in a background threadzPolling is already runningNFT)targetdaemonz!Email polling started (interval: z	 seconds))rk   rg   r   warningrj   clearr   Thread_polling_looprh   startr&   rc   rO   s    r   start_pollingEmailPoller.start_polling   s    ZZ}}##$@A Z
 !DM""$$++43E3EdSDLLL KK@yYZ ZZs   .CB	C
C$timeoutc                 r   U R                      U R                  (       d%  U R                  R                  S5         SSS5        gU R                  R	                  S5        SU l        U R
                  R                  5         SSS5        U R                  (       a  U R                  R                  5       (       ao  U R                  R                  US9  U R                  R                  5       (       a  U R                  R                  S5        gU R                  R	                  S5        g! , (       d  f       N= f)	zk
Stop the polling service gracefully

Args:
    timeout: Maximum time to wait for thread to stop (seconds)
zPolling is not runningNFzStopping email polling...r   z*Polling thread did not stop within timeoutzEmail polling stoppedT)
rk   rg   r   r|   r&   rj   setrh   is_alivejoin)r   r   s     r   stop_pollingEmailPoller.stop_polling   s     ZZ==##$<= Z
 KK89!DM  "  <<DLL1133LLg.||$$&&##$PQ    !89! Zs   .D(<D((
D6r;   c                 h    U R                      U R                  sSSS5        $ ! , (       d  f       g= f)z%Check if polling is currently runningN)rk   rg   rO   s    r   
is_runningEmailPoller.is_running   s    ZZ== ZZs   #
1c                 &   U R                   R                  S5        U R                  (       aN   U R                  5         U R                  R                  U R                  S9(       a  O U R                  (       a  MN  U R                   R                  S5        g! [         ao  nU R                   R                  SU 3SS9  [        U5      U l
        U R                  R                  [        SU R                  5      S9(       a   SnAM   SnANSnAff = f)	z0Main polling loop that runs in background threadzPolling loop startedr   zError in polling loop: Texc_inforS   NzPolling loop ended)r   r&   rg   check_for_new_emailsrj   waitrc   r(   r)   rX   ro   min)r   r,   s     r   r   EmailPoller._polling_loop   s    /0mm))+ ##(((? @ mmm" 	-.  !!$;A3"?$!O"%a&##((R1G(H I	s   8B 
D!ADDc                 @    U R                   R                  S5        SnU R                  (       a<   [        R                  " U R                  5      nU[        SS9-
  R                  S5      nU R                  R                  U R                  U R                  SSUS9nU R                   R                  S	[        U5       S
U R                   35        SnU Ht  nUR                  S5      nU(       d  M  U R                  R                  U5      (       d  U R!                  U5        US-  nMU  U R                   R                  SU S35        Mv     US:  a  U R                   R                  SU S35        [        R"                  " 5       U l        U R$                  R'                  5       U l        U =R(                  U-  sl        SU l        U R-                  5         g!    GNq= f! [.         a3  nU R                   R1                  SU 3SS9  [3        U5      U l        e SnAff = f)z%Check for new emails and process themzChecking for new emails...N   )minutesz%Y-%m-%dFd   )ra   rb   unread_onlytop	from_datezFound z email(s) from r   idzEmail z already processed, skippingz
Processed z new email(s)zError checking for new emails: Tr   )r   rB   rm   r   rH   r   strftimer`   check_emails_from_senderra   rb   r&   r'   r%   rf   r=   process_new_emailr5   rl   r6   rn   ro   rw   r(   r)   rX   )r   r   
last_checkemailsnew_emails_countemailr:   r,   s           r   r    EmailPoller.check_for_new_emails   s   4	KK:; I((!)!7!78Q8Q!RJ!+i.B!B L LZ XI
 ''@@!..{{!# A F KKvc&k]/$BSBSATUV  ! 99T? ||66x@@**51$)$KK%%xj8T&UV    !#  :.>-?}!MN $,<<>D (,(<(<(F(F(HD%''+;;'"DOKL  	KK ?sCdS!!fDO	s/   .G  ;G ,E+G  GG   
H*.HHr   c                    UR                  S5      nUR                  SS5      nU R                  R                  SU SU 35         U R                  R	                  X!5        UR                  S0 5      nU R                  R                  S5        U R                  R                  SU 35        U R                  R                  S	UR                  S
S5       35        U R                  R                  SUR                  SS5       35        U(       a  U R                  R                  SUR                  SS5       35        U R                  R                  SUR                  SS5       35        U R                  R                  SUR                  SS5       35        U R
                  (       a  U R                  U5        gg! [         a&  nU R                  R                  SU SU 3SS9  e SnAff = f)zJ
Process a new email

Args:
    email: Email dictionary from EmailChecker
r   subjectz
No SubjectzProcessing new email: z - extracted_detailszEmail processed successfully:z  Subject: z  From: fromUnknownz  Date: datez  Name: namezN/Az	  Phone: phonez	  Email: r   zError processing email z: Tr   N)	r%   r   r&   rf   rC   rd   _call_callbackr(   r)   )r   r   r:   r   r   r,   s         r   r   EmailPoller.process_new_email"  s    99T?))I|41(3wiHI	LL--h> !&		*=r BKK<>KK{7)45KKx		&)(D'EFGKKx		&)(D'EFG   8,=,A,A&%,P+Q!RS  9->-B-B7E-R,S!TU  9->-B-B7E-R,S!TU   ##E* !  	KK 7zA3GRVW	s   E5F= =
G-!G((G-c                    U R                   (       d  g SSKnUR                  U R                   U[        R                  " 5       R                  5       SS.SS9nUR                  5         U R                  R                  SU R                    S35        g! [         a5  nU R                  R                  SU R                    S	U 35         SnAgSnAff = f)
z*Call external callback URL with email dataNr   email_poller)r   rA   source
   )r#   r   zCallback to z successfulz	 failed: )rd   requestspostr   r5   r6   raise_for_statusr   r&   r(   r|   )r   r   r   responser,   s        r   r   EmailPoller._call_callbackF  s      	P}}!!"!)!9!9!;,
  % H %%'KK|D,=,=+>kJK 	PKK,t/@/@.A1# NOO	Ps   A=B 
C+CCc                 p    US:  a  [        S5      eXl        U R                  R                  SU S35        g)zUpdate polling intervalr   z$Interval must be at least 10 secondszPolling interval updated to z secondsN)
ValueErrorrc   r   r&   )r   rc   s     r   set_intervalEmailPoller.set_interval[  s6    b=CDD 7zJKr   c           
      "   U R                      U R                  U R                  U R                  U R                  U R
                  U R                  U R                  U R                  R                  5       S.sSSS5        $ ! , (       d  f       g= f)zGet current polling status)r   rc   rl   rn   ra   rb   ro   processed_emails_trackedN)
rk   rg   rc   rm   rn   ra   rb   ro   rf   rP   rO   s    r   
get_statusEmailPoller.get_statusb  s_    ZZ"mm MM#'#<#<*.*E*E $ 1 1++"oo,0LL,L,L,N	 ZZs   A)B  
B)rk   rg   rj   rh   rd   r`   rn   rb   rc   rl   rm   ro   r   ra   rp   rf   )zscheduling@usetwine.cominbox<   rR   N)   )r   rT   rU   rV   rW   r	   rX   rZ   r   r   rq   rw   r   r   rY   r   r   r   r   r   r   r   r   r[   r\   r   r   r^   r^   b   s    6HaQh/3#l ## ##25#KN#'}#J?"
:C 2!D !
/.6p"t "HPD P*LS LD r   r^   )r   timer#   r   r   r   typingr   r   r   r   r   fetchr	   r   r^   r\   r   r   <module>r      s9       	 ( , ,  T. T.nL Lr   