
    i                    	   U d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlmZ ddlmZ ddlmZmZ ddlmZ ddlmZmZ dd	lmZ dd
lmZmZ ddlmZ ddlmZ ddlmZ ddl m!Z!m"Z" ddl#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z) ddl*m+Z+m,Z,m-Z-m.Z. ddl/m0Z0m1Z1 ddl2m3Z3m4Z4m5Z5m6Z6m7Z7m8Z8  ee9          :                                j;        d         Z< e'              e0e<          Z= e1e<          Z>e=dz  Z?e=dz  Z@e=dz  ZAdZB e4e=e?            e            ZC e            ZD e            ZE e            ZF e            ZG e            ZH e            ZIi ZJdeKd<   dddddddddddddZLd eKd!<   ddddddddddd"
ZMd eKd#<   d$dd%ddddddddddd&ZNd eKd'<   d(ZOd)ZPd*ZQd+ZRdZSdZTi ZUd,eKd-<   d.ZVd/ZWeWZXd0ZYh d1ZZh d2Z[ddd8Z\dd;Z]dd=Z^ e\d>d$          Z_ e` eja        d?d                    b                                Zc e` eja        d@d                    b                                Zd e` eja        dAd                    b                                Ze efecoedpee          Zg ehdB ei eja        dC e`eW                    peW                    Zj ehdB ei eja        dD e`eX                    peX                    Zk e` eja        dEd                    b                                Zl e] eja        dFdG                    Zm e` eja        dHd                    b                                n                                Zo e` eja        dId                    b                                Zp e^ eja        dJd                    ZqdKZrdLZsddMZtddOZuddPZvddQZwddSZxddUZyddWZzddYZ{ddd\Z|dd]Z}dd`Z~ddaZddcZddeZddfZddhZddiZddlZddmZddoZddpZdddsZddwZddxZddyZddd{Zdd~ZddZdddddZddZddZeSeTddddZddZdddZddZdddZddZdddZddZdddZdddZddZdddZddZddZddZePfddZdddddZdddZdddZdddZeRfddZddZddZddZddZddZ G d de          ZddZedk    r e e                      dS )z/Local server for website + AI intel ingest API.    )annotationsN)SimpleCookie)	timedelta)datetimetimezone)
HTTPStatus)SimpleHTTPRequestHandlerThreadingHTTPServer)Path)LockThread)parse_qs)urlparse)uuid4)fetch_pokemon_latest_newstranslate_pokemon_news_payload)add_classification_feedbackadd_manual_tweetfeedback_memory_statsload_environmentset_manual_selectionsync_accounts)get_website_backup_status restore_website_data_from_backuprun_website_backupstart_website_backup_scheduler)get_website_data_dirsetup_website_storage)build_i18n_feed_bundle_asyncconfigure_i18n_runtimei18n_state_snapshotlocalized_feed_from_bundlequeue_i18n_retranslatetranslate_texts   zx_intel_feed.jsonzx_intel_jobs.jsonzpokemon_latest_news.jsonzh-Hantzdict[str, dict[str, str]]SESSIONSidle Fg      ?   )status
started_atfinished_atlast_success_at
last_errortriggerduration_msschedule_enabledschedule_interval_hoursschedule_window_daysnext_run_atlast_scheduled_atzdict[str, object]
SYNC_STATE)
r+   r,   r-   r.   r/   r0   r1   changedskippedreasonBACKUP_STATETmanual_only)okrestoredr:   r+   r,   r-   r.   r/   r0   r1   forcebranchsubdirRESTORE_STATEx   2   <      zdict[str, dict]POKEMON_NEWS_STATEintel_admin_sessionip  iat1>   http://127.0.0.1:3000http://127.0.0.1:8787http://localhost:3000http://localhost:8787>   /api/intel/pick/api/intel/sync/api/intel/backup/api/intel/restore/api/intel/feedback/api/intel/job-status/api/intel/analyze-url/api/intel/retranslatenamestrdefaultboolreturnc                    t          t          j        | d                                                                                    }|st          |          S |dv S )Nr)   >   1yonyestrue)rW   osgetenvstriplowerrY   )rV   rX   raws      /app/scripts/ai_intel_server.py	_env_flagrg   y   sS    
bib!!
"
"
(
(
*
*
0
0
2
2C G}}111    re   
str | Nonec                    t          | pd                                                                          }|dv r|                                S dS )NLax>   laxnonestrict)rW   rc   rd   
capitalize)re   values     rf   _normalize_samesiterq      sN    u##%%++--E)))!!!5rh   set[str]c                    t          | pd                                          }|st          t                    S d |                    d          D             }t          t                                        |          S )Nr)   c                ^    g | ]*}|                                 |                                 +S  rc   .0xs     rf   
<listcomp>z*_parse_allowed_origins.<locals>.<listcomp>   s-    ===117799=QWWYY===rh   ,)rW   rc   setDEFAULT_ALLOWED_ORIGINSsplitunion)re   textpartss      rf   _parse_allowed_originsr      sn    syb>>!!D ,*+++==

3===E&''--e444rh   INTEL_AUTH_REQUIREDINTEL_ADMIN_USERINTEL_ADMIN_PASS_HASHINTEL_ADMIN_PASSi,  INTEL_SESSION_TTL_SECONDSINTEL_AUTH_TOKEN_TTL_SECONDSINTEL_AUTH_TOKEN_SECRETINTEL_COOKIE_SAMESITErk   INTEL_COOKIE_SECUREINTEL_COOKIE_DOMAININTEL_ALLOWED_ORIGINS   i@  c                 b    t          j        t          j                                                  S N)r   nowr   utc	isoformatru   rh   rf   _now_isor      s     <%%//111rh   bytesc                v    t          j        |                               d                              d          S )Nutf-8=)base64urlsafe_b64encodedecoderstripre   s    rf   _b64url_encoder      s/    #C((//88??DDDrh   c                    t          | pd                                          }|sdS dt          |           dz  z  }t          j        | |                     d                    S )Nr)   rh   r      r   )rW   rc   lenr   urlsafe_b64decodeencode)re   r   paddings      rf   _b64url_decoder      sj    syb>>!!D sc$iiZ!^$G#t$6W$6$6$=$=g$F$FGGGrh   c                     t           rt           S t           dt          pt           d} t	          j        |                     d                                                    S )N|z|renaiss-intel-token-v1r   )TOKEN_SECRET_ENVAUTH_USERNAMEAUTH_PASSWORD_HASHAUTH_PASSWORD_PLAINhashlibsha256r   	hexdigest)seeds    rf   _auth_token_secretr      sU      ``0G4G```D>$++g..//99;;;rh   usernamec                   t          t          j                              }dt          | pd                                          ||t          z   d}t          j        |dd                              d          }t          |          }t          j
        t                                          d          |                    d          t          j                                                  }t           d| dt          |           S )	Nr%   r)   )vuiatexpF)r{   :)ensure_ascii
separatorsr   .)inttimerW   rc   TOKEN_TTL_SECONDSjsondumpsr   r   hmacnewr   r   r   digestAUTH_TOKEN_PREFIX)r   now_tspayloadpayload_rawpayload_b64	signatures         rf   _create_auth_tokenr      s    FR  &&(())	 G *W5ZPPPWWX_``K --K+--44W=={?Q?QRY?Z?Z\c\jkkrrttIKK+KKy0I0IKKKrh   tokenc                &   t          | pd                                          }|sdS |                    d          }t          |          dk    rdS |\  }}}|t          k    rdS 	 t          j        t                                          d          |                    d          t          j
                                                  }t          |          }t          j        ||          sdS t          |          }t          j        |                    d                    }	n# t"          $ r Y dS w xY wt%          |	t&                    sdS t)          |	                    d          pd          dk    rdS t)          |	                    d          pd          }
|
t)          t-          j                              k    rdS t          |	                    d	          pd                                          }|sdS t.          r|t.          k    rdS |S )
Nr)   r      r   r   r   r%   r   r   )rW   rc   r~   r   r   r   r   r   r   r   r   r   r   compare_digestr   loadsr   	Exception
isinstancedictr   getr   r   )r   re   r   prefixr   signature_b64expected_sig
actual_sigr   r   
expires_atr   s               rf   _verify_auth_tokenr      s   
ekr


 
 
"
"C rIIcNNE
5zzQr).&FK"""rx  ''00w''N
 
 &((	 	
 $M22
"<<< 	2$[11*[//8899   rrgt$$ r
7;;s q!!Q&&rW[['',1--JS%%%%r7;;s##)r**0022H r ]22rOs   &BD' 06D' '
D54D5raw_passwordc                d   t          | pd          }t          sdS t          rlt          }|                    d          r|                    dd          }t          |          dk    rdS |\  }}}}	 t          |          }n# t          $ r Y dS w xY wt          j	        d|
                    d          |
                    d          |          }	t          j        |	                                |          S |                    d	          r|                    dd
          }t          |          dk    rdS |\  }}}t          j        | | 
                    d                                                    }	t          j        |	|          S t           rt          j        |t                     S dS )Nr)   Fzpbkdf2_sha256$$r   r   r   r   zsha256$   )rW   AUTH_CONFIGUREDr   
startswithr~   r   r   r   r   pbkdf2_hmacr   r   r   hexr   r   r   )
r   passwordencodedr   _iterations_rawsaltexpected_hex
iterationsr   s
             rf   _verify_passwordr      s   <%2&&H u =$.// 
	CMM#q))E5zzQu491A~t\ 00

   uu(8??73K3KT[[Y`MaMacmnnF&vzz||\BBBi(( 	=MM#q))E5zzQu$)!At\^t$7X$7$7$>$>w$G$GHHRRTTF&v|<<< B"8-@AAA5s   2B 
BBdatetime | Nonec                *   t          | pd                                          }|sd S 	 t          j        |          }n# t          $ r Y d S w xY w|j         |                    t          j                  }|	                    t          j                  S )Nr)   )tzinfo)
rW   rc   r   fromisoformatr   r   replacer   r   
astimezone)re   r   dts      rf   _parse_iso_utcr   	  s    syb>>!!D t#D))   tt	yZZx|Z,,==&&&s   > 
AAnow_isoNonec                   | rt          |           nt          j        t          j                  }g }t
                                          D ]S\  }}t          t          |                    d          pd                    }|r||k    r|	                    |           T|D ]}t
          
                    |d            d S )Nr   r)   )r   r   r   r   r   r'   itemsrW   r   appendpop)r   now_dtstalesiddatar   s         rf   _purge_sessions_unlockedr     s    (/O^G$$$X\(,5O5OFE^^%%  	TS,!7!7!=2>>?? 	cVmmLL    S$   rh   c                ~   t          j        t          j                  }|t	          t
                    z   }t          j        d          }t          5  t          |
                                           | |
                                |
                                dt          |<   d d d            n# 1 swxY w Y   |S )Nseconds    )r   
created_atr   )r   r   r   r   r   SESSION_TTL_SECONDSsecretstoken_urlsafeSESSIONS_LOCKr   r   r'   )r   r   expiresr   s       rf   _create_sessionr  !  s    
,x|
$
$CI&9::::G!"%%E	 
 
 111 --//!++--
 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Ls   AB22B69B6
session_iddict | Nonec                F   t          | pd                                          }|sd S t          5  t                       t                              |          }t          |t                    s	 d d d            d S t          |          cd d d            S # 1 swxY w Y   d S Nr)   )rW   rc   r  r   r'   r   r   r   )r  r   states      rf   _get_sessionr  /  s    
jB


%
%
'
'C t	   """S!!%&& 		       
 E{{                 s   ?B;BBBc                    t          | pd                                          }|sd S t          5  t                              |d            d d d            d S # 1 swxY w Y   d S r
  )rW   rc   r  r'   r   )r  r   s     rf   _delete_sessionr  ;  s    
jB


%
%
'
'C 	    S$                                   s   AAAr   c                 T   t                                           sdi iS 	 t          j        t                               d                    } n# t
          $ r di icY S w xY wt          | t                    sdi iS |                     d          }t          |t                    si }d|iS )Njobsr   encoding)		JOBS_PATHexistsr   r   	read_textr   r   r   r   )re   r  s     rf   _read_jobs_unlockedr  C  s     |j,,g,>>??   |c4   |776??DdD!! D>s   -A AAr  c                    t                      | d}t          j                            dd           t                              t          j        |dd          d           d S )	N)
updated_atr  Tparentsexist_okFr   r   indentr   r  )r   r  parentmkdir
write_textr   r   )r  r   s     rf   _write_jobs_unlockedr!  R  s`    %ZZ66G4$777G%JJJU\]]]]]rh   c                   t          |           t          k    rd S t          |                                 d d          }t	          |d t                             }|                                  |                     |           d S )Nc                Z    t          | d         pi                     d          pd          S )Nr%   r   r)   rW   r   )kvs    rf   <lambda>z%_trim_jobs_unlocked.<locals>.<lambda>[  s*    #r!u{6G6G6U6U6[Y[2\2\ rh   Tkeyreverse)r   MAX_JOB_ITEMSsortedr   r   clearupdate)r  orderedkeeps      rf   _trim_jobs_unlockedr0  X  st    
4yyM!!TZZ\\'\'\fjkkkG'((DJJLLLKKrh   langc                8   t          | pd                                                                          }|sdS |                    d          s|dv rdS |                    d          rdS |                    d          rdS |                    d          rdS dS )	Nr)   r&   zzh-hant>   zh-hkzh-mozh-twzhzh-Hanskoen)rW   rc   rd   r   )r1  re   s     rf   _normalize_lang_tagr:  a  s    
djb//


!
!
'
'
)
)C y
~~i   C+F$F$Fy
~~d y
~~d t
~~d t9rh   c                    t                               |           }t          |t                    s%| ddddt          t                      d}|t           | <   |S )NFr)   )r1  
refreshinglast_refresh_atr/   next_refresh_atinterval_minutesr  )rG   r   r   r   %DEFAULT_POKEMON_NEWS_INTERVAL_MINUTESr   )r1  r  s     rf   _state_for_lang_unlockedrA  p  s^    ""4((EeT"" 
)!! E"**
 
 $)4 Lrh   fieldsobjectc                    t          |           }t          5  t          |          }|                    |           t	                      |d<   t          |          cd d d            S # 1 swxY w Y   d S )Nr  )r:  POKEMON_NEWS_STATE_LOCKrA  r-  r   r   )r1  rB  tagr  s       rf   _update_news_staterG    s    
d
#
#C	   (--V&jjlE{{	                 s   AA((A,/A,c                    t          |           }t          5  t          t          |                    cd d d            S # 1 swxY w Y   d S r   )r:  rE  r   rA  )r1  rF  s     rf   _get_news_staterI    s    
d
#
#C	  3 3,S11223 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3s   A  AAr   c                z    t          |           }t          j        dd|          }|st          S t          d| dz  S )Nz[^a-zA-Z0-9_-]r   pokemon_latest_news_z.json)r:  resubPOKEMON_NEWS_CACHE_PATH	DATA_ROOT)r1  rF  safes      rf   _news_cache_pathrQ    sH    
d
#
#C6#S#..D '&&9d99999rh   c                     t                                           si S 	 t          j        t                               d                    } n# t
          $ r i cY S w xY wt          | t                    r| ni S Nr   r  )	FEED_PATHr  r   r   r  r   r   r   r   s    rf   _read_feed_snapshotrU    s     	j,,g,>>??   			S$''/33R/s   -A AArp   r   c                `    	 t          |           S # t          $ r t          |          cY S w xY wr   )r   r   )rp   rX   s     rf   	_safe_intrW    s@    5zz   7||s    --cards
list[dict]hoursc                @   | sdS t          j        t          j                  }t	          t          dt          |                              }d}| D ]K}t          |t                    st          |
                    d                    }|s=||z
  |k    r|dz  }L|S )Nr   r%   rZ  published_at)r   r   r   r   r   maxr   r   r   r   r   )rX  rZ  r   windowtotalcardr   s          rf   _count_recent_cardsrb    s     q
,x|
$
$CSCJJ//000FE  $%% 	DHH^4455 	8vQJELrh   c                    d }| D ]G}t          |t                    st          |                    d                    }|s=|||k    r|}H|r|                                ndS )Nr]  r)   )r   r   r   r   r   )rX  latestra  r   s       rf   _latest_card_timere    s~    "F  $%% 	DHH^4455 	>R&[[F!'/6R/rh   c                 l    t           5  t          t                    cd d d            S # 1 swxY w Y   d S r   )SYNC_STATE_LOCKr   r7   ru   rh   rf   _sync_state_snapshotrh    sw    	    J                                      )--r0   c                    t           5  dt          d<   t                      t          d<   dt          d<   dt          d<   t          | pd          t          d<   d	t          d
<   d d d            d S # 1 swxY w Y   d S )Nrunningr+   r,   r)   r-   r/   manualr0   r   r1   )rg  r7   r   rW   r0   s    rf   _start_sync_statern    s    	 & &(
8#+::
< $&
=!#%
<  #G$7x 8 8
9$%
=!& & & & & & & & & & & & & & & & & &s   AA--A14A1started_monotonicfloatc                b   t                      }t          dt          t          t	          j                    t          |           z
  dz                                }t          5  dt          d<   |t          d<   |t          d<   dt          d<   |t          d	<   d d d            d S # 1 swxY w Y   d S )
Nr     r=   r+   r-   r.   r)   r/   r1   )	r   r^  r   roundr   	monotonicrp  rg  r7   )ro  r   r1   s      rf   _finish_sync_state_okru    s    jjGaUDN$4$4u=N7O7O$OSW#WXXYYZZK	 0 0#
8$+
=!(/
$%#%
< $/
=!0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0s   $3B$$B(+B(error_messagec                l   t                      }t          dt          t          t	          j                    t          |           z
  dz                                }t          5  dt          d<   |t          d<   t          |pd          t          d<   |t          d<   d d d            d S # 1 swxY w Y   d S )	Nr   rr  failedr+   r-   unknown_errorr/   r1   )
r   r^  r   rs  r   rt  rp  rg  r7   rW   )ro  rv  r   r1   s       rf   _finish_sync_state_failedrz    s    jjGaUDN$4$4u=N7O7O$OSW#WXXYYZZK	 0 0'
8$+
=!#&}'G#H#H
< $/
=!	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0s   $8B))B-0B-)r5   r6   enabledinterval_hourswindow_daysr5   r6   c                    t           5  t          |           t          d<   t          |          t          d<   t	          |          t          d<   |r
|t          d<   |r
|t          d<   d d d            d S # 1 swxY w Y   d S )Nr2   r3   r4   r5   r6   )rg  rY   r7   rp  r   r{  r|  r}  r5   r6   s        rf   _mark_sync_scheduler    s     
 @ @)-g
%&05n0E0E
,--0-=-=
)* 	4(3J}% 	@.?J*+@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @s   AA33A7:A7accountslist[str] | Nonedaysc           	     L   t          j                    }t          |           	 t          | t	          dt          |                              }t          |dg d           t          |           |S # t          $ r#}t          |t          |                      d }~ww xY w)Nrm  r%   )r  r}  Fr9  r8  r7  r?   target_langs)r   rt  rn  r   r^  r   r   ru  r   rz  rW   )r  r  r0   ro  result
sync_errors         rf   _run_intel_syncr    s    ((g&&&&c!SYY>O>OPPP$V5G^G^G^____/000   !"3S__EEEs   AA6 6
B# BB#c                    t                                           sd S 	 t          j        t                               d                    } n# t
          $ r Y d S w xY wt          | t                    sd S t          | dg d           d S )Nr   r  Fr  r  )	rT  r  r   r   r  r   r   r   r   )feeds    rf   _warm_i18n_bundle_from_feedr  	  s     z)--w-??@@   dD!!  UAXAXAXYYYYYYs   -A 
AAr|  r}  run_on_startupr  c                   t          dt          | pt                              t          dt          |pt                              t          t          dz  dz                      |rdnt          j        t          j	                  t                    z                                   }t          d|           dfd
}t          |d                                           d S )N      ?r%   rE   rF   r   T)r{  r|  r}  r5   rZ   r   c                    } 	 t          j        t          dt          |                                t	                      }t          j        t          j                  t                    z   
                                }t          d||           t          5  t          t                              d          pd                                                                          dk    }d d d            n# 1 swxY w Y   |r-t          5  dt          d	<   d d d            n# 1 swxY w Y   } 2	 t%          d d
           n# t&          $ r Y nw xY w} Y)NTr%   r   r  r+   r)   rk  z/scheduled sync skipped: another sync is runningr/   	scheduledr  r  r0   )r   sleepr^  r   r   r   r   r   r   r   r   r  rg  rW   r7   r   rc   rd   r  r   )delayscheduled_atnext_run
is_runningfirst_delayinterval_secondssafe_interval_hourssafe_window_dayss       rf   _loopz&_start_x_sync_scheduler.<locals>._loop'  s   	%Js1c%jj))***#::L X\22YGW5X5X5XXcceeH2,$".    ! ^ ^ !9!9!?R@@FFHHNNPPT]]
^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ $ a a/`J|,a a a a a a a a a a a a a a a(4DkZZZZZ   $E-	%s7   AC<<D D D''D+.D+7E
 

EEtargetdaemonrZ   r   )r^  rp  DEFAULT_X_SYNC_INTERVAL_HOURSr   DEFAULT_X_SYNC_WINDOW_DAYSrs  r   r   r   r   r   r   r  r   start)	r|  r}  r  
first_nextr  r  r  r  r  s	        @@@@rf   _start_x_sync_schedulerr    s    dE.*Y<Y$Z$Z[[1c+"K1KLLMM5!4r!9B!>??@@%;!!+;K,x|,,y/M/M/MMXXZZJ*$	   % % % % % % % % %4 %%%%++-----rh   c                 l    t           5  t          t                    cd d d            S # 1 swxY w Y   d S r   )BACKUP_STATE_LOCKr   r;   ru   rh   rf   _backup_state_snapshotr  D  sw    	 " "L!!" " " " " " " " " " " " " " " " " "ri  c                P   t           5  dt          d<   t                      t          d<   dt          d<   dt          d<   t          | pd          t          d<   d	t          d
<   dt          d<   dt          d<   dt          d<   d d d            n# 1 swxY w Y   t	          j                    S )Nrk  r+   r,   r)   r-   r/   rl  r0   r   r1   Fr8   r9   r:   )r  r;   r   rW   r   rt  rm  s    rf   _start_backup_stater  I  s    	 	$ 	$!*X%-ZZ\"&(]#%'\""%g&9":":Y&']#"'Y"'Y!#X	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ >s   A6B

BBr  c                4   t                      }t          dt          t          t	          j                    t          |          z
  dz                                }t          |                     d                    }t          |                     d                    }t          5  |rdn|rdndt          d<   |t          d<   |t          d<   t          |                     d	                    t          d	<   |t          d<   t          |                     d
          pd          t          d
<   |rdn8t          |                     d          p|                     d
          pd          t          d<   |r
|t          d<   d d d            d S # 1 swxY w Y   d S )Nr   rr  r=   r9   rx  r+   r-   r1   r8   r:   r)   errorry  r/   r.   )r   r^  r   rs  r   rt  rp  rY   r   r  r;   rW   )r  ro  r   r1   r=   r9   s         rf   _finish_backup_stater  W  s   jjGaUDN$4$4u=N7O7O$OSW#WXXYYZZK	fjj		B6::i(())G	 	6 	6)+!Uw2T))HX&-]#&1]#"&vzz)'<'<"="=Y")Y!$VZZ%9%9%?R!@!@X+-%vRR3vzz'7J7J7ufjjYaNbNb7ufu3v3v\" 	6.5L*+	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6 	6s   (CFFFrl  c                    t           5  t          t                              d          pd          dk    r	 d d d            dS 	 d d d            n# 1 swxY w Y   d
 fd}t	          |d	                                           dS )Nr+   r)   rk  FrZ   r   c                     t                    } t          t          t          j                  }t          ||            d S )Nrm  r:   )r  r   rO  ROOTr  r  )startedr  r0   s     rf   _workerz&_spawn_website_backup.<locals>._workerm  s?    %g666#It{7KKKVW-----rh   Tr  r  )r  rW   r;   r   r   r  )r0   r  s   ` rf   _spawn_website_backupr  h  s    	  |))/R00I==       =              . . . . . .
 '$'''--///4s   /AAAc                 l    t           5  t          t                    cd d d            S # 1 swxY w Y   d S r   )RESTORE_STATE_LOCKr   rB   ru   rh   rf   _restore_state_snapshotr  v  sw    	 # #M""# # # # # # # # # # # # # # # # # #ri  r?   c                B   t           5  dt          d<   t                      t          d<   dt          d<   dt          d<   t          | pd          t          d<   d	t          d
<   t	          |          t          d<   d d d            n# 1 swxY w Y   t          j                    S )Nrk  r+   r,   r)   r-   r/   rl  r0   r   r1   r?   )r  rB   r   rW   rY   r   rt  r0   r?   s     rf   _start_restore_stater  {  s    	 - -"+h&.jjl#')m$&(l##&w':(#;#;i '(m$!%eg- - - - - - - - - - - - - - - >s   A/BB
Bc                   t                      }t          dt          t          t	          j                    t          |          z
  dz                                }t          |                     d                    }t          |                     d                    }|rdnd}t          5  t                              ||t          |                     d          p	|rdn|rdnd          |||t          |                     d          pt                              d          pd	          t          |                     d
          pt                              d
          pd	          |rd	n8t          |                     d          p|                     d          pd          d	           |r
|t          d<   d d d            d S # 1 swxY w Y   d S )Nr   rr  r=   r>   rx  r:   restore_failedr@   r)   rA   r  ry  )	r=   r>   r:   r+   r-   r1   r@   rA   r/   r.   )r   r^  r   rs  r   rt  rp  rY   r   r  rB   r-  rW   )r  ro  r   r1   r=   r>   r+   s          rf   _finish_restore_stater    s    jjGaUDN$4$4u=N7O7O$OSW#WXXYYZZK	fjj		BFJJz**++H%TTXF	 7 7$fjj22vX7uzz\^TtTXTXdtww &*fjj22Wm6G6G6Q6QWUWXXfjj22Wm6G6G6Q6QWUWXX$&obbC

70C0C0nvzzRZG[G[0n_n,o,o
 
	
 	
 	
  	7/6M+,7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7s   .DG		GGc                \    t           5  t          t                              d          pd                                                                          dk    r	 d d d            dS 	 d d d            n# 1 swxY w Y   d
 fd}t          |d	                                           dS )Nr+   r)   rk  FrZ   r   c                     t                    } t          t          t          j        dt                              }t          ||            d S )Nr  T)rl  force_override)r  r   rO  r  r  rY   r  )r  r  r?   r0   s     rf   r  z'_spawn_website_restore.<locals>._worker  sU    &weDDD1K;;	
 
 
 	fg.....rh   Tr  r  )r  rW   rB   r   rc   rd   r   r  )r0   r?   r  s   `` rf   _spawn_website_restorer    s   	  }  **0b117799??AAYNN       N              / / / / / / / '$'''--///4s   AA77A;>A;   limitc           
     d   t           5  t                      }d d d            n# 1 swxY w Y   t          |t                    r|                    d          ni }t          |t                    r|ni }g }|                                D ]:}t          |t                    s|                    t          |                     ;|                    d d           |d t          dt          t          |           d                             }ddddd}|                                D ]t}t          |t                    st          |                    d	          pd
                                                                          }||v r||xx         dz  cc<   u|t          |          |dS )Nr  c                J    t          |                     d          pd          S )Nr   r)   r$  )ry   s    rf   r&  z(_collect_jobs_snapshot.<locals>.<lambda>  s    Cl 3 3 9r:: rh   Tr'  r%   (   r   )queuedrk  donerx  r+   r)   )countsr`  r   )	JOBS_LOCKr  r   r   r   valuesr   sortr^  minr   rW   rc   rd   r   )r  r  jobs_maprowsitemr  rowr+   s           rf   _collect_jobs_snapshotr    s   	 & &#%%& & & & & & & & & & & & & & &$.ud$;$;Cuyy   H%h55=xx2HD!!    $%% 	DJJII::DIIII-#aSZZ,,---.D	 F       #t$$ 	SWWX&&,"--3355;;==V6NNNaNNNX  s   #''c                 T   t           5  t          t                                                    } d | D             }d d d            n# 1 swxY w Y   |D ]}t	          |                    d                    }t          |          }t          |                    d          pt          j	        d          pd          
                                }||d<   t          |                    d          t                    r$t          |                    d          pg           nd|d<   |S )Nc                F    g | ]}t          t          |                    S ru   )r   rA  )rx   r1  s     rf   rz   z0_collect_news_state_snapshot.<locals>.<listcomp>  s)    GGG-d3344GGGrh   r1  providerNEWS_SEARCH_PROVIDERminimax_cli_searchr   r   )rE  r+  rG   keysr:  r   _read_news_cacherW   ra   rb   rc   r   listr   )langsr  r  rF  cacher  s         rf   _collect_news_state_snapshotr    sa   	  H H)..0011GGGGGH H H H H H H H H H H H H H H  d d!#''&//22 %%uyy,,i	:P0Q0QiUijjpprr"J8B599WCUCUW[8\8\cs599W--3444bcGKs   3AAA
   c                   t                      }|                    d          }t          |t                    r|ng }d |D             }t	                      }t          |           }t                      }t                      }t                      }	t          t                    }
t                      }t                      }|                    d          }t          |t                    r|ni }t          |                    d          pd                                          }t!          |          }t#          |d          }t#          |d	          }t%          |                    d
i                               d                    }t%          |                    d
i                               d                    }t          |                    d          pd                                                                          dk    }t          |                    d          t                    r|                    d          ni }t)                      i dt          |                    d          pd          dt          |                    d          pd          dt          |                    d          pd          dt          |                    d          p|          dt          |                    d          pd          dt          |                    d          pd          dt%          |                    d          d          dt+          |                    d                    dt-          |                    d          pt.                    dt%          |                    d          t0                    dt          |                    d          pd          dt          |                    d          pd          d|d|dt%          |                    d          t3          |                    dt%          |                    d          t3          |                    d t%          |                    d           t%          |                    d          t3          |                              t%          |                    d!          d          t%          |                    d"          d          t%          |                    d#          d          t%          |                    d$          d          t%          |                    d%          pi                     d&          d          t%          |                    d%          pi                     d'          d          |||d(	||||||||z   |rd)ndz   t+          ||z   |rd)ndz             d*|	d+t          |                    d          pd          d,|pd- d.t          |                    d          pd-           d/t-          |                    d          pt.                    d0d1t%          |                    d          t3          |                     d2d3|dk    rdn	|dk    rdndd4| d5| d2d6t5          d7 |D                       rdndd8t3          |           d2d9t          |                    d          pd          d8d:                    d; |                    d<          pg D                       pd- d=t          |                    d>          pd-           d2d?d@dAt%          |	                    dB          d          t%          |	                    dC          d          z    dDt%          |	                    dE          d           dFt%          |	                    dG          d           d2dHt          |                    d          p|
                    dI          rdIndJ          dK|
                    dL           dM|
                    dN           dO|
                    dP          rdQndR dS|                    dT           d2dUt          |                    d          pd          dVt+          |                    dW                     dXt          |                    dY          pd-           dZt          |                    d[          p|
                    d[          pd-           d2gd\t+          |                    dI                    t+          |                    d]                    t%          |                    d^          d          d_ |                    d`          pg D             t          |                    da          t                    r|                    da          ni db |                    dc          pg D             d d         ddid<|i|i t8          de|ii |
df|idgS )hNrX  c                <    g | ]}t          |t                    |S ru   )r   r   rw   s     rf   rz   z'_build_admin_status.<locals>.<listcomp>  s'    999qZ4%8%89999rh   r  discord_monitorgenerated_atr)      r\     r  rk  r  r+   pipeline_countsr(   r,   r-   r.   r/   r0   r1   r   r2   r3   r4   r5   r6   feed_generated_atlatest_source_attotal_cardsraw_total_cardssource_total_cardsexcluded_cardsexcluded_by_selectionexcluded_by_feedbackexcluded_by_source_preferencededupe_stats
ai_removedlocal_removed)	r  r  r  r  dedupe_ai_removeddedupe_local_removedr  new_cards_6hnew_cards_24hr%   )r  r  sync_runningqueued_jobsrunning_jobspending_processingis_processingx_sync_agentz
last_sync=z--z next=z
 interval=gzh cards=)rV   r+   detailurl_analyzer_agentzqueued=z	 running=pokemon_news_agentc              3  Z   K   | ]&}t          |                    d                     V  'dS )r<  N)rY   r   rw   s     rf   	<genexpr>z&_build_admin_status.<locals>.<genexpr>4  s6      *Z*Z4l0C0C+D+D*Z*Z*Z*Z*Z*Zrh   zlangs=i18n_feed_agentr{   c                ,    g | ]}t          |          S ru   )rW   rw   s     rf   rz   z'_build_admin_status.<locals>.<listcomp>:  s    ,],],]SVV,],],]rh   r  z source=source_generated_atfeedback_memory_agentactivezrules=rulesdefault_rulesz
 feedback=feedback_itemsz
 profiles=source_profileswebsite_backup_agentr{  disabledz	provider=r  z subdir=rA   z repo=has_repor|   unsetz	 changed=r8   website_restore_agentz	restored=r>   z reason=r:   z branch=r@   discord
configuredcards_totalc                n    g | ]2}t          |                                          #t          |          3S ru   rW   rc   rw   s     rf   rz   z'_build_admin_status.<locals>.<listcomp>U  s9    jjj1[^_`[a[a[g[g[i[ijAjjjrh   channel_idschannel_statsc                n    g | ]2}t          |                                          #t          |          3S ru   r  rw   s     rf   rz   z'_build_admin_status.<locals>.<listcomp>W  s9    ```aQTUVQWQWQ]Q]Q_Q_`3q66```rh   errors)r{  r  r  r  r  r  restoreruntime)server_timesyncr  	new_postsmemoryagentsmonitorsnewsi18nstoragebackup)rU  r   r   r  rh  r  r  r!   r   r   rO  r  r  r   rW   rc   re  rb  rW  rd   r   rY   rp  r  r  r   anyjoinSTORAGE_STATE)r  r  	cards_rawrX  	card_rows
sync_stater  news_states
i18n_statememory_statsbackup_statusbackup_staterestore_statediscord_infor  r  	recent_6h
recent_24hr  r  r  r  s                         rf   _build_admin_statusr7    s     D!!I%/	4%@%@H		bE99E999I%''J!...D.00K$&&J(**L-i88M)++L+--M88-..L#-lD#A#AI<<rLtxx//5266<<>>L(33#IQ777I$Yb999JTXXh3377	BBCCLDHHXr2266x@@AAKz~~h//5266<<>>DDFF)SL5?IZ@[@[]a5b5bjdhh0111hjO  zz
c*..22<f==
#jnn\::@bAA
 3z~~m<<BCC
 s:>>2C#D#D#TUU	

 #jnn\::@bAA
 s:>>)44:;;
 9Z^^M%B%BAFF
 Z^^4F%G%G H H
 &uZ^^<U-V-V-wZw'x'x
 #Ijnn=S.T.TVp$q$q
 3z~~m<<BCC
  Z^^4G%H%H%NB!O!O
  
  0
 9TXXm%<%<c)nnMM
  y2C)D)Dc)nnUU!
" !)DHH5I,J,JIVZV^V^_pVqVqsv  xA  tB  tB  MC  MC  #D  #D#
$ (1A(B(BAFF%.txx8O/P/PRS%T%T$-dhh7M.N.NPQ$R$R-6txx@_7`7`bc-d-d!*DHH^,D,D,J+O+OP\+]+]_`!a!a$-txx/G/G/M2.R.RSb.c.cef$g$g.%'5
 
 
8 %'(&("-"<\@XWX"Y!+"<\@XWX"YZZ
 
  'jnnX66@&AAR!5 R R
} = = EFFR R %jnn5N&O&O&pSp q qwR R 'txx'>'>IOOR R		 	 -'3a'7'7))+XY//hh_eHKHH,HH  -'**Z*Zk*Z*Z*Z'Z'Zf))`f53{#3#355  *jnnX66@&AA e388,],]jnnW>U>U>[Y[,],],]#^#^#fbf  e  epst~  uC  uC  DY  uZ  uZ  ub  ^b  qc  qc  e  e  0" x9\-=-=g-F-F#J#JYWcWgWghwWxWxz{M|M|#|  x  x  IR  S_  Sc  Sc  dt  Su  Su  wx  Iy  Iy  x  x  EN  O[  O_  O_  `q  Or  Or  tu  Ev  Ev  x  x  /l..x88w-J[J[\eJfJf=vYYlvxx Lm&7&7
&C&C  L  L]M^M^_gMhMh  L  L  zG  zK  zK  LV  zW  zW  qdpupu  ]d  L  L  o{  o  o  @I  oJ  oJ  L  L  0m//99CVDDh]%6%6z%B%B C C h h!-"3"3H"="="EFFh h!-"3"3H"="="dARARS[A\A\"d`deeh h	 I-
^  0 0 ; ;<<"<#3#3L#A#ABB()9)9-)H)H!LLjj1A1A-1P1P1VTVjjjFPQ]QaQabqQrQrtxFyFy  "B!1!1/!B!B!B  @B``L,<,<X,F,F,L"```acbcacd 	
 [
 

}
 


|
 
Sm m mrh   pathc                    |                                  si S 	 t          j        |                     d                    }n# t          $ r i cY S w xY wt          |t                    r|ni S rS  )r  r   r   r  r   r   r   )r8  re   s     rf   _read_news_cache_unlockedr:  i  sx    ;;== 	j99::   			S$''/33R/s   (A AAc                    t          |           }t          5  t          |          }d d d            n# 1 swxY w Y   t          |t                    rt	          |          ni S r   )rQ  POKEMON_NEWS_LOCKr:  r   r   )r1  r8  re   s      rf   r  r  s  s    D!!D	 . .'--. . . . . . . . . . . . . . ."3--5499925s   377r   c                    | j                             dd           |                     t          j        |dd          d           d S )NTr  Fr   r  r   r  )r  r  r   r   r   )r8  r   s     rf   _write_news_cache_unlockedr>  z  sK    KdT222OODJwU1EEEPWOXXXXXrh   r  max_age_minutesc           	        t          |                     d          pd                                          }|sdS 	 t          j        |                    dd                    }n# t          $ r Y dS w xY wt          j        t          j	                  }||
                    t          j	                  z
  }|t          t          dt          |                              k    S )Nr  r)   FZz+00:00r%   )minutes)rW   r   rc   r   r   r   r   r   r   r   r   r   r^  r   )r  r?  updatedr   r   ages         rf   _is_news_cache_freshrE    s    %))N++1r2288::G u#GOOC$B$BCC   uu
,x|
$
$C
hl++
+C)C3+?+?$@$@AAAAAs   (A% %
A32A3pendingrefresh_startedrG  rH  c               T   t          | t                    rt          |           ni }|s7dt          t          j        d          pd          t          |          dg dd}t          |          }t          |                    d                    |d<   t          |          |d<   t          |          |d<   t          |                    d	          pd          |d	<   t          |                    d
          pd          |d
<   t          |                    d          pt                    |d<   t          |                    d          pd                                          }|r>t          |                    d          pd                                          sd| |d<   |S )Nr)   r  r  rG  F)r  r  r1  summary_moder   cachedr<  rH  r=  r>  r?  auto_interval_minutesr/   warningu   背景更新最近失敗：)r   r   rW   ra   rb   r:  rI  rY   r   r   r@  rc   )r  r1  rG  rH  baser  state_errors          rf   _compose_news_payloadrP    s   $UD1194;;;rD 
BI&<==UAUVV'--%
 
 D!!Eeii5566D"?33D	7mmDO!%)),=">">"D"EED	!%)),=">">"D"EED	$'		2D(E(E(nIn$o$oD	 !eii--344::<<K F3txx	228b99??AA FEEEYKrh   	max_itemsc           
     b   t          |          }t          |          }t          5  t          |          }d d d            n# 1 swxY w Y   | s'|r%t	          |          rt          |          }d|d<   |S |t          k    rt          t                    }t          5  t          |          }d d d            n# 1 swxY w Y   | s|rt	          |          s-t          d|t                    }|                    dd            	 t          ||          }	t          5  t          ||	           d d d            n# 1 swxY w Y   d|	d<   |	S # t          $ r+}
|r#t          |          }d|d<   d|
 |d<   |cY d }
~
S  d }
~
ww xY w	 t          t          dt          t          |          d                    t          	          }t          5  t          ||           d d d            n# 1 swxY w Y   d|d<   |S # t          $ r+}
|r#t          |          }d|d<   d
|
 |d<   |cY d }
~
S  d }
~
ww xY w)NTrK  r?   rQ  r1  Fu'   新聞翻譯失敗，已回退快取：rM  r      )rQ  r1  u'   最新抓取失敗，已回退快取：)r:  rQ  r<  r:  rE  r   POKEMON_NEWS_CANONICAL_LANG_get_pokemon_newsr   r   r>  r   r   r^  r  r   )r?   rQ  r1  lang_tag
cache_pathr  r   	base_path
base_cache
translatedexcfreshs               rf   rV  rV    s   "4((H!(++J	 6 6)*556 6 6 6 6 6 6 6 6 6 6 6 6 6 6 U 3E:: u++ ...$%@AA	 	> 	>29==J	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	> 	+
 	+*>z*J*J 	+*QlmmmJNN8T***	7
HMMJ" C C*:zBBBC C C C C C C C C C C C C C C#(Jx  	 	 	 u++$(!%Ts%T%T	"	)!SY4455,
 
 
  	: 	:&z5999	: 	: 	: 	: 	: 	: 	: 	: 	: 	: 	: 	: 	: 	: 	:h    	5kkG $GH!P3!P!PGINNNNNNs   AA	AB99B= B=E D9-E 9D==E  D=	E 
F E;4F :E;;F AG9 
G'G9 'G++G9 .G+/	G9 9
H.H)"H.(H))H.r:   delay_secondsc                <   t          |           }|dk    r!t          j        t          |                     	 t	          d||          }t          |                    d          pd                                          }t          |                    d          pt                                                                pt                      }t          |d|||           d S # t          $ r+}t          |dt          |          |	           Y d }~d S d }~ww xY w)
Nr   TrS  rM  r)   r  F)r<  r=  r/   last_reason)r<  r/   r`  )r:  r   r  rp  rV  rW   r   rc   r   rG  r   )	r1  rQ  r:   r^  rF  r$  rM  
refresh_atr\  s	            rf   _refresh_pokemon_news_workerrb    s!   
d
#
#Cq
5''((([ tysKKKdhhy))/R006688.11?XZZ@@FFHHVHJJ
35*Y`ntuuuuuu [ [ [35SXXSYZZZZZZZZZZ[s   B,C& &
D0 DDc           	        t          |           }t          5  t          |          }t          |                    d                    r	 d d d            dS d|d<   ||d<   t                      |d<   d d d            n# 1 swxY w Y   t          t          |||t          |pd          fd          	                                 dS )Nr<  FTr`  r  r   r  argsr  )
r:  rE  rA  rY   r   r   r   rb  rp  r  )r1  rQ  r:   r^  rF  r  s         rf   _spawn_pokemon_news_refreshrf    s$   
d
#
#C	  ) )(--		,''(( 	) ) ) ) ) ) ) ) #l%m&jjl) ) ) ) ) ) ) ) ) ) ) ) ) ) ) .c9feTaTfefNgNg5hquvvv||~~~4s   3A>A>>BBr?  r  	list[str]c                  	
 t          dt          |                     

dz  g 	|D ],}t          |          }|r|	vr	                    |           -	sdg	t	          j        t          j                  t                    z   	                                }t          	          D ]-\  }}t          |
|           t          |d|dz             .d	
fd}t          |d                                           d S )Nr%   rE   r&   r   r?  r>  startup   rQ  r:   r^  rZ   r   c                 ,   	 t          j                   t          j        t          j                  t                    z                                   } t                    D ]-\  }}t          ||            t          |d|dz             .)NTr   ri  r  rk  rl  )r   r  r   r   r   r   r   r   	enumeraterG  rf  )r  idxr1  r  rQ  normalized_langssafe_interval_mins      rf   r  z,_start_pokemon_news_scheduler.<locals>._loop  s    	sJ'((( X\22YGW5X5X5XXcceeH&'788 s s	T"4:K]effff+DIkiloqiqrrrrr	srh   Tr  r  )r^  r   r:  r   r   r   r   r   r   r   rn  rG  rf  r   r  )r?  r  rQ  r1  rF  r  ro  r  r  rp  rq  s     `     @@@rf   _start_pokemon_news_schedulerrr    se   As#34455(2- ) )!$'' 	)3...##C((( '%;,x|,,yAQ/R/R/RR]]__J/00 i i	T42CU_````#DIi_beg_ghhhhhs s s s s s s s s %%%%++-----rh   urlc                L   t           5  t                      }|d         }t                      j        d d         }|| ddt	                      t	                      d}|||<   t          |           t          |           t          |          cd d d            S # 1 swxY w Y   d S )Nr  r  r  u   已排入背景分析佇列。)idrs  r+   messager   r  )r  r  r   r   r   r0  r!  r   )rs  r  r  job_idjobs        rf   _create_jobry    s    	  #%%V}SbS!7"**"**
 
 VD!!!T"""Cyy                 s   BBB Brw  c                r   t           5  t                      }|d         }|                    |           }t          |t                    s	 d d d            d S |                    |           t                      |d<   ||| <   t          |           t	          |          cd d d            S # 1 swxY w Y   d S )Nr  r  )r  r  r   r   r   r-  r   r!  )rw  rB  r  r  re   s        rf   _update_jobr{    s   	 
 
#%%V}hhv#t$$ 	
 
 
 
 
 
 
 
 	

6$JJLVT"""Cyy
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
s   AB,AB,,B03B0c                    t           5  t                      }|d                             |           }t          |t                    rt	          |          nd cd d d            S # 1 swxY w Y   d S )Nr  )r  r  r   r   r   )rw  r  re   s      rf   _get_jobr}  )  s    	 < <#%%Fm''&sD11;tCyyyt< < < < < < < < < < < < < < < < < <s   AA$$A(+A(c                   t          | dt                      d           	 t          |          }t          |t                    r"t          |                    d                    s5t          |pi                     d          pd          }t          |          t          |                    d          t                    r|                    d          ni }t          |                    d          t                    r|                    d          ni }|rt          |d	g d
           t          | dt                      d|                    d          |                    d          |                    d                     d S # t          $ r;}t          | dt                      d| t          |                     Y d }~d S d }~ww xY w)Nrk  u0   背景分析進行中，可安全刷新頁面。)r+   r,   rv  r=   r  u   分析失敗tweetr  Fr  r  r  u'   分析完成，已加入精選卡片。ru  rs  r  )r+   r-   rv  tweet_id	tweet_urlr  rx  u   分析失敗：)r+   r-   rv  r  )r{  r   r   r   r   rY   r   rW   RuntimeErrorr   r   )rw  rs  r  err_msgr  r  r\  s          rf   _run_analyze_jobr  0  s   yXZZI{||||
!#&&&$'' 	(tFJJt4D4D/E/E 	(6<R,,W55GHHGw''''1&**W2E2Et'L'LT

7###RT%/

60B0BD%I%IQvzz&!!!r 	b(UI`I`I`aaaa 

=YYt__ii&&.11	
 	
 	
 	
 	
 	
  
 
 
 

+c++c((	
 	
 	
 	
 	
 	
 	
 	
 	
 	

s   E*F 
G0GGc                    t          |           }t          t          t          |d                   | fd                                           |S )Nru  Trd  )ry  r   r  rW   r  )rs  rx  s     rf   _enqueue_analyze_jobr  N  sE    
c

C
"#c$i..#)>tLLLRRTTTJrh   c                       e Zd Z fdZd dZd dZd dZd!dZd"d
Zd#dZ	d dZ
d dZd dZd dZd$dZd"dZej        dfd%dZd&dZd!dZd! fdZd!dZ xZS )'Handlerc                b     t                      j        |dt          t                    i| d S )N	directory)super__init__rW   r  )selfre  kwargs	__class__s      rf   r  zHandler.__init__U  s0    $>#d))>v>>>>>rh   rZ   rW   c                z    t          t          | j                  j        pd                                          pdS )N/)rW   r   r8  rc   r  s    rf   _request_pathzHandler._request_pathX  s3    8DI&&+2s3399;;BsBrh   c                x    t          | j                            d          pd                                          S )NOriginr)   )rW   headersr   rc   r  s    rf   _request_originzHandler._request_origin[  s1    4<##H--344::<<<rh   c                b    |                                  }|sdS dt          v r|S |t          v r|ndS )Nr)   *)r  ALLOWED_ORIGINS)r  origins     rf   _allowed_originzHandler._allowed_origin^  sD    %%'' 	2/!!M?22vv:rh   r   c                   |                                  }|sd S |                     d|           |                     dd           |                     dd           |                     dd           |                     dd	           d S )
NzAccess-Control-Allow-Originz Access-Control-Allow-Credentialsr`   zAccess-Control-Allow-MethodszPOST, GET, OPTIONSzAccess-Control-Allow-Headersz-Content-Type, Authorization, X-Requested-WithVaryr  )r  send_header)r  allow_origins     rf   _set_cors_headerszHandler._set_cors_headersf  s    ++-- 	F6EEE;VDDD79MNNN79hiii*****rh   rY   c                   t           dv rdS t           dv rdS t          | j                            d          pd                              d          d                                                                         }|d	k    rdS dS )
N>   r\   r]   r^   r_   r`   T>   0nnoofffalseFzX-Forwarded-Protor)   r{   r   https)COOKIE_SECURE_ENVrW   r  r   r~   rc   rd   )r  protos     rf   _is_secure_cookiezHandler._is_secure_cookiep  s     ???4 @@@5DL$$%899?R@@FFsKKANTTVV\\^^G4urh   r  c                   t            d| dddt           dt           g}t          r|                    dt                      |                                 r|                    d           d                    |          S )	Nr   Path=/HttpOnly	SameSite=zMax-Age=Domain=Secure; )AUTH_COOKIE_NAMECOOKIE_SAMESITEr  COOKIE_DOMAINr   r  r)  )r  r  r   s      rf   _session_cookie_headerzHandler._session_cookie_headerz  s    ..*..))),*,,
  	4LL2=22333!!## 	#LL"""yyrh   c                    t            ddddt           ddg}t          r|                    dt                      |                                 r|                    d           d	                    |          S )
Nr   r  r  r  z	Max-Age=0z%Expires=Thu, 01 Jan 1970 00:00:00 GMTr  r  r  )r  r  r  r   r  r)  )r  r   s     rf   _clear_session_cookie_headerz$Handler._clear_session_cookie_header  s    """)))3
  	4LL2=22333!!## 	#LL"""yyrh   c                x   t          | j                            d          pd                                          }|sdS t	                      }	 |                    |           n# t          $ r Y dS w xY w|                    t                    }|sdS t          |j        pd                                          S )NCookier)   )	rW   r  r   rc   r   loadr   r  rp   )r  
cookie_rawjarnodes       rf   _session_id_from_cookiezHandler._session_id_from_cookie  s    ))(339r::@@BB
 	2nn	HHZ     	 	 	22	ww'(( 	24:#$$**,,,s   A% %
A32A3c                n   t          | j                            d          pd                                          }|sdS |                    dd          }t          |          dk    rdS |d                                                                         dk    rdS |d                                         S )NAuthorizationr)    r%   r   r   bearer)rW   r  r   rc   r~   r   rd   )r  rp   r   s      rf   _auth_token_from_headerzHandler._auth_token_from_header  s    DL$$_55;<<BBDD 	2C##u::??28>>!!##x//2Qx~~rh   c                    t           sdS t          |                                           }|r|S |                                 }|sdS t	          |          }|sdS t          |                    d          pd                                          S )Nadminr)   r   )AUTH_REQUIREDr   r  r  r  rW   r   rc   )r  
token_userr  r  s       rf   _current_userzHandler._current_user  s     	7'(D(D(F(FGG
 	1133
 	2Z(( 	2599Z((.B//55777rh   r   c           	         t           sddt          ddddt          dS t          s
ddddddd	d
S |                                 }dddt	          |          |ddt          dS )NTFr  openBearerr=   auth_requiredauth_configuredauthenticatedusermode
token_typetoken_ttl_secondsr)   misconfigureduM   INTEL_ADMIN_USER / INTEL_ADMIN_PASS_HASH 未設定，管理功能已鎖定。)r=   r  r  r  r  r  r  	protected)r  r   r   r  rY   )r  r  s     rf   _auth_me_payloadzHandler._auth_me_payload  s     
	!&#2!%&%6	 	 	  		!%#(!&'h   !!##!#!$ZZ"!2	
 	
 		
rh   c                    t           sdS t          s&|                     dddt          j                   dS |                                 rdS |                     ddddt          j                   dS )NTFR   管理帳號尚未設定。請設定 INTEL_ADMIN_USER 與 INTEL_ADMIN_PASS_HASH。r=   r  r+   u<   需要登入管理員帳號才能查看或執行此操作。)r=   r  r  )r  r   
_send_jsonr   SERVICE_UNAVAILABLEr  UNAUTHORIZEDr  s    rf   _require_admin_accesszHandler._require_admin_access  s     	4 	OOq  "5     5 	4W!% 
 * 	 	
 	
 	
 urh   Nr   r+   r   extra_headers-dict[str, str] | list[tuple[str, str]] | Nonec                2   t          j        |d                              d          }|                     t	          |                     |                                  |                     dd           |                     dd           |                     dt          t          |                               t          |t                    rK|                                D ]5\  }}|                     t          |          t          |                     6nMt          |t                    r8|D ]5\  }}|                     t          |          t          |                     6|                                  | j                            |           d S )	NF)r   r   zContent-Typezapplication/json; charset=utf-8zCache-Controlzno-storeContent-Length)r   r   r   send_responser   r  r  rW   r   r   r   r   r  end_headerswfilewrite)r  r   r+   r  re   r(  rp   s          rf   r  zHandler._send_json  su    ju555<<WEE3v;;'''   )JKKK*555)3s3xx==999mT** 	7+1133 7 7
U  S3u::66667t,, 	7+ 7 7
U  S3u::6666
rh   r8  c                @    |t           vrdS |                                 S )NT)PROTECTED_POST_PATHSr  )r  r8  s     rf   _require_adminzHandler._require_admin  s%    +++4))+++rh   c                    |                      t          j                   |                                  |                                  d S r   )r  r   
NO_CONTENTr  r  r  s    rf   
do_OPTIONSzHandler.do_OPTIONS  sC    :0111   rh   c                    |                                  }|dk    r)|                     |                                            d S |dk    r|                                 sd S 	 t	          | j                  j        }t          |d          }t          |	                    d          pdgd         d          }t          |	          }|                     d
|d           n># t          $ r1}|                     dd| dt          j                   Y d }~nd }~ww xY wd S |dk    rt                                          s&|                     dddt          j                   d S 	 t	          | j                  j        }t          |d          }t#          |	                    d          pdgd         pd          }n# t          $ r d}Y nw xY w	 t%          j        t                              d                    }n?# t          $ r2}|                     dd| dt          j                   Y d }~d S d }~ww xY wt+          |t,                    s&|                     dddt          j                   d S t/          ||          }	|                     d
|	t1          |          d           d S |                    d          r&|                     dddt          j                   d S t5                                                       d S )Nz/api/auth/mez/api/intel/admin-statusF)keep_blank_valuesr  10r   r  r  T)r=   r+   zfailed to build admin status: r  r  z/api/intel/feedzfeed not foundr1  r&   r   r  zfailed to read feed: zfeed format invalid)r=   r  r1  z/api/	Not found)r  r  r  r  r   r8  queryr   rW  r   r7  r   r   INTERNAL_SERVER_ERRORrT  r  	NOT_FOUNDrW   r   r   r  r   r   r"   r:  r   r  do_GET)r  r8  r  paramsr  status_payloadr\  request_langr  localized_feedr  s             rf   r  zHandler.do_GET  sm   !!##>!!OOD1133444F,,,--// I ++1!%5AAA!6::g#6#6#@4&!"DbII!45!A!A!At~ F FGGGG I I Iu7]X[7]7] ^ ^gq  hH  I  I  I  I  I  I  I  IIF$$$##%% u7G H HQ[Qefff) ++1!%5AAA"FJJv$6$6$E9+q#I#VYWW ) ) )()z)"5"5w"5"G"GHH   u7Ts7T7T U U^h^~ dD)) u7L M MV`Vvwww7lKKNOO4I\]iIjIjkklllF??7## 	OO5;??
H\O]]]FsD   "A>C! !
D+'DD(AG   GG-H 
H='H88H=c                   |                                  }|dvr&|                     dddt          j                   d S t	          | j                            dd          pd          }|dk    r| j                            |          nd	}	 |r't          j
        |                    d
                    ni }n# t          $ r i }Y nw xY w|dk    r]t          |                    d          pd                                          }t          |                    d          pd          }t          s4t!          d          }|                     ddddddt"          |d           d S t$          s&|                     dddt          j                   d S |t(          k    st+          |          s&|                     dddt          j                   d S t/          |          }t!          |          }|                     dddd|ddt"          |d	d|                     |          i           d S |dk    rq|                                 }t5          |           |                     dt          t$          ddt          rdnddt"          dd|                                 i           d S |dk    rt9          |                    d                    }	|                    d          }
t;          |
t<                    s&|                     dddt          j                   d S g }|
d t@                   D ]_}t          |pd                                          }|s|!                    d           =|!                    |d tD                              `tG          ||	           \  }}|                     d|	||d!           d S | $                    |          sd S 	 |d"k    r|                    d#          }t;          |t<                    rd$ |D             }nd }t	          |                    d%d&          pd&          }| %                                pd'}tM          |tO          d(|          |)          }|                     d|d*           d S |d+k    rt          |                    d,          pd                                          }t          |                    d-          pd                                          (                                }t          |                    d.          pd                                          }tS          ||          }d }|d/k    r|rtU          |d/|0          }tW                      }tY          |dg d12           |                     d|||d3           d S |d4k    rt          |                    d,          pd                                          }t          |                    d5          pd                                          (                                }t          |                    d.          pd                                          }tU          |||0          }tW                      }tY          |dg d12           |                     d||d6           d S |d7k    rt          |                    d,          pd                                          }|s&|                     dd8dt          j                   d S t[          |          }|s&|                     dd9dt          j                   d S |                     d|d:           d S |d;k    rKt]          | %                                pd'<          }|                     d|t_                      d=           d S |d>k    rota          |                    d?d                    }tc          | %                                pd'|@          }|                     d|te                      dA           d S |dBk    rt          |                    d          pd                                          (                                }|r|dCk    rg d1}ndD |3                    dE          D             }ti                      } t;          | tj                    r(t;          |                     dF          t<                    s&|                     ddGdt          j                   d S tm          | |H          }|                     d|to                      dI           d S |dJk    r5ta          |                    d?                    }t	          |                    dKdL          pdL          }!t          |                    d          pd                                          pdM}	tq          |	          }"|p|" pts          |"           }#|#r2tu          |	tO          dNtw          |!dO                    |rd'ndPQ          nd}|"r(tk          |"          }$d|$dR<   ty          |$|	d|S          }%nty          i |	d|S          }%dT|%dU<   |                     d|%dV           d S t          |                    dW          pd                                          }&|&s&|                     ddXdt          j                   d S t{          |&          }|                     d|d:           d S # t          $ r<}'|                     dt          |'          dt          j>                   Y d }'~'d S d }'~'ww xY w)YN>   /api/auth/loginrN   rO   /api/auth/logoutrP   rQ   rR   rS   rT   rU   /api/intel/pokemon-news/api/intel/translate-textsFr  r  r  r  r  r   s   {}r   r  r   r)   r   r  Tr  r  )r=   r  r  r  r  r  r  r   r  u   帳號或密碼錯誤r  )	r=   r  r  r  r  r  r  r  r   z
Set-Cookie)r  r  r  r  r1  textsztexts must be an array)r1  )r=   r1  r  r   rO   r  c                    g | ]W}t          |                                          #t          |                                                              d           XS )@)rW   rc   lstriprw   s     rf   rz   z#Handler.do_POST.<locals>.<listcomp>  sO    ___qPSTUPVPVP\P\P^P^_A 5 5c : :___rh   r  r*   rl  r%   r  )r=   r  rN   ru  actionr:   excluder  r  r  )r=   	selectionfeedbackr  rR   label)r=   r  r  rS   zid is requiredzjob not found)r=   rx  rP   rm  )r=   r  r'  rQ   r?   r  )r=   r  r  rU   allc                ^    g | ]*}|                                 |                                 +S ru   rv   rw   s     rf   rz   z#Handler.do_POST.<locals>.<listcomp>  s-    #X#X#X!aggii#XAGGII#X#X#Xrh   r{   rX  zfeed not ready)r  )r=   retranslater%  r  rQ  rF   r&   r   rT  z	on-demand)rQ  r:   rK  rF  u6   背景更新中，稍後會自動顯示最新消息。rv  )r=   r$  rs  zurl is required)?r  r  r   r  r   r  r   rfilereadr   r   r   r   rW   rc   r  r   r   r   r  r   r   r  r  r  r  r  r  r:  r   r  BAD_REQUESTTRANSLATE_MAX_ITEMSr   TRANSLATE_MAX_CHARSr$   r  r  r  r^  rd   r   r   r   r   r}  r  r  rY   r  r  r~   rU  r   r#   r!   r  rE  rf  r  rP  r  r  )(r  r8  lengthbodyr   r   r   r   r   r1  	raw_textsr  r  r   r[  r  r  r  r0   r  r  r  r:   r  r  r  rw  rx  r  r?   lang_rawr  r  rQ  r  needs_refreshcache_payloadr$  rs  r\  s(                                           rf   do_POSTzHandler.do_POSTB  s/   !!## 
 
 
 OO5;??
H\O]]]FT\%%&6<<CDD*01**tzv&&&%	:>FdjW!5!5666BGG 	 	 	GGG	 $$$7;;z228b99??AAH7;;z228b99H  *733").)- ' &&.->!&	 	   " #!u  &9      =((0@0J0J(u7N O OXbXoppp!(++C&x00EOO%)'+%)$'"*):"
 
  ,T-H-H-M-MN     F%%%..00CC   OO%2'6%*+8DKKf"*):	 	  ,T-N-N-P-PQ     F///&w{{6':':;;DG,,Ii.. u7O P PYcYoppp D!"6#6"67 8 84:2,,.. KKOOOD!5"5!567777.t$???JOO  '	    F""4(( 	Fa	g(((";;z22h-- $__H___HH#H7;;vr228b99,,..:(((QW^___tV < <===(((w{{4006B77==??W[[228b99??AAGGIIW[[228b99??AA06BB	Y&&6&:8YW]^^^H&,V5OfOfOfggggt)QYci j jkkk,,,w{{4006B77==??GKK006B77==??EEGGW[[228b99??AA6xvVVV&,V5OfOfOfggggt6 R RSSS...W[[..4"55;;== OO5;K$L$LU_UkOlllFv&& OO5?$K$KT^ThOiiiFtC 8 8999***/8J8J8L8L8XPXYYYtKaKcKc d deee+++W[[$778809K9K9M9M9YQYafgggtLcLeLe f fggg///w{{6228b99??AAGGII Y8u#4#4#:#:#:LL#X#Xx~~c7J7J#X#X#XL*,,!$-- Z@Q@QSW5X5X OO5;K$L$LU_UsOtttF/<PPPtFL_LaLa b bccc000W[[1122K ; ; @qAA	7;;v..4"55;;==J(.. % Ye) Y=QRW=X=X9X Q^  i5dc!SQZ\^M_M_F`F`uz  jLiqiq  AL  M  M  M  M  di _$(KKM.2M(+0eelmmmDD0T4Y`aaaD&^DOtT : :;;;gkk%((.B//5577C u7H I IR\Rhiii&s++COO44455555 	g 	g 	gOO53s88<<ZEeOfffffffff	gs{   +B8 8CCB2l D#l 0Dl :A"l 5l l /Al  A3l 5C*l !6l D:l Al 3'l 
m"&1mm"rZ   rW   r  )rZ   rY   )r  rW   rZ   rW   rZ   r   )r   r   r+   r   r  r  rZ   r   )r8  rW   rZ   rY   )__name__
__module____qualname__r  r  r  r  r  r  r  r  r  r  r  r  r  r   OKr  r  r  r  r  __classcell__)r  s   @rf   r  r  T  s       ? ? ? ? ?C C C C= = = =; ; ; ;+ + + +                 - - - -	  	  	  	 8 8 8 8 
  
  
  
D   6 (]GK	    *, , , ,
   
) ) ) ) ) )VXg Xg Xg Xg Xg Xg Xg Xgrh   r  c                 	   t          j        d          } |                     ddd           |                     dt          dd	
           |                     dt          t          d
           |                     ddd           |                     dt
          t          t          j        dt          t                              pt                    d
           |                     dt          t          t          j        dt          t                              pt                    d
           |                     ddt          dd          d           |                     ddt          dd           d           |                                 }d  t          |j        pd!                              d"          D             }t          t!          d#t          |j                            |t$          $           |j        rSt)          dt!          d%t          |j                            t!          d#t          |j                            &           ndt/          t!          d%t          |j                            t!          d#t          |j                            t1          |j                  '           t5                       t7          t8          t:          j                   t?          |j         |j!        ftD                    }tG          d(t:           d)|j          d*|j!                    tG          d+t8           d,t1          tH          %                    d-                     d.t1          tH          %                    d/                                tG          d0t!          d#t          |j                             d1|pdg            |j        rtG          d2           nktG          d3t!          d%t          |j                            d4d5t!          d#t          |j                             d6t1          |j                              tG          d7           tG          d8tL           d9tN           d:tP          rd;nd< d=tS          tT                                	 |+                                 n# tX          $ r Y nw xY w|-                                 n# |-                                 w xY wd>S )?Nz!Run local website AI intel server)descriptionz--hostz	127.0.0.1z	Bind host)rX   helpz--portiS"  z	Bind port)typerX   r   z--news-interval-minutesz-Pokemon news auto refresh interval in minutesz--news-langsr&   z7Comma separated language tags for background news cachez--sync-interval-hoursX_SYNC_INTERVAL_HOURSz+X/Discord intel auto sync interval in hoursz--sync-window-daysX_SYNC_WINDOW_DAYSz#X/Discord intel sync window in daysz--sync-run-on-startup
store_trueX_SYNC_RUN_ON_STARTUPFz3Run one X/Discord sync shortly after server startup)r  rX   r   z--no-sync-schedulerX_SYNC_ENABLEDTz0Disable automatic X/Discord intel sync schedulerc                    g | ]D}t          |                                          #t          |                                          ES ru   r  rw   s     rf   rz   zmain.<locals>.<listcomp>=  s:    ]]]cRSffllnn]SVV\\^^]]]rh   r)   r{   r%   )r?  r  rQ  r  )r{  r|  r}  r  z[ai-intel] serving z at http://r   z[ai-intel] storage data_root=z	 symlink=using_symlinkz
 migrated=migratedz+[ai-intel] pokemon news auto refresh every z minutes; langs=z'[ai-intel] X/Discord auto sync disabledz%[ai-intel] X/Discord auto sync every r  z hours; window_days=z
; startup=a  [ai-intel] API endpoints: GET /api/auth/me, POST /api/auth/login, POST /api/auth/logout, GET /api/intel/feed, GET /api/intel/admin-status, POST /api/intel/sync, POST /api/intel/analyze-url, POST /api/intel/pick, POST /api/intel/feedback, POST /api/intel/job-status, POST /api/intel/backup, POST /api/intel/restore, POST /api/intel/retranslate, POST /api/intel/pokemon-news, POST /api/intel/translate-textsz[ai-intel] auth mode: required=z, configured=z, user=r|   r  z, allowed_origins=r   ).argparseArgumentParseradd_argumentr   r@  rp  ra   rb   rW   r  r  rg   
parse_args
news_langsr~   rr  r^  news_interval_minutesDEFAULT_POKEMON_NEWS_MAX_ITEMSno_sync_schedulerr  sync_interval_hourssync_window_daysr  rY   sync_run_on_startupr  r   rO  r  r  r
   hostportr  printr*  r   r  r   r   r+  r  serve_foreverKeyboardInterruptserver_close)parserre  r  servers       rf   mainr=    s   $1TUUUF
+KHHH
sD{KKK
1Ej  ra  b  b  b
	@yzzz
bi 7=Z9[9[\\}`}~~:	     BI2C8R4S4STTrXrss2	     1599B	     .555?	     D]]S)>B%?%?%E%Ec%J%J]]]E!3q#d>X:Y:Y3Z3Zbg  tR  S  S  S  S 
tU4+C%D%DEEAs4#899::	
 	
 	
 	
 	
 	 tU4+C%D%DEEAs4#899:: 899	
 	
 	
 	

  !!!"9dk::: $)TY!7AAF	
H
H
H
H
HTY
H
HIII		:	: 	:)-m.?.?.P.P)Q)Q	: 	:**:6677	: 	:  
 
  HAs4C]?^?^8_8_  H  Hqv  rF  |E  {F  H  H  I  I  I 
788888uT%=>>??K8 8q#d&;"<"<==8 8 D4558 8	
 	
 	
 
	*   
	5!	5 	50?	5 	5&3G	5 	5 "/22	5 	5      	1s*   &R; :S  ;
SS  SS   S6__main__)F)rV   rW   rX   rY   rZ   rY   )re   ri   rZ   rW   )re   ri   rZ   rr   r  )re   r   rZ   rW   )re   rW   rZ   r   )r   rW   rZ   rW   )r   rW   rZ   rW   )r   rW   rZ   rY   )re   ri   rZ   r   r   )r   ri   rZ   r   )r  rW   rZ   r  )r  rW   rZ   r   r  )r  r   rZ   r   )r1  ri   rZ   rW   )r1  rW   rZ   r   )r1  rW   rB  rC  rZ   r   )r1  ri   rZ   r   )r   )rp   rC  rX   r   rZ   r   )rX  rY  rZ  r   rZ   r   )rX  rY  rZ   rW   )r)   )r0   rW   rZ   r   )ro  rp  rZ   r   )ro  rp  rv  rW   rZ   r   )r{  rY   r|  rp  r}  r   r5   rW   r6   rW   rZ   r   )r  r  r  r   r0   rW   rZ   r   r  )r|  rp  r}  r   r  rY   rZ   r   )r0   rW   rZ   rp  )r  r   ro  rp  rZ   r   )rl  )r0   rW   rZ   rY   )r)   F)r0   rW   r?   rY   rZ   rp  )rl  T)r0   rW   r?   rY   rZ   rY   )r  )r  r   rZ   r   )rZ   rY  )r  )r8  r   rZ   r   )r1  ri   rZ   r   )r8  r   r   r   rZ   r   )r  r   r?  r   rZ   rY   )
r  r   r1  rW   rG  rY   rH  rY   rZ   r   )FrF   N)r?   rY   rQ  r   r1  ri   rZ   r   )
r1  rW   rQ  r   r:   rW   r^  rp  rZ   r   )
r1  rW   rQ  r   r:   rW   r^  rp  rZ   rY   )r?  r   r  rg  rQ  r   rZ   r   )rs  rW   rZ   r   )rw  rW   rB  rC  rZ   r  )rw  rW   rZ   r  )rw  rW   rs  rW   rZ   r   )rZ   r   )__doc__
__future__r   r*  r   r   r   r   ra   rL  r  r   http.cookiesr   r   r   r   httpr   http.serverr	   r
   pathlibr   	threadingr   r   urllib.parser   r   uuidr   minimax_newsr   r   x_intel_corer   r   r   r   r   r   website_backupr   r   r   r   website_storager   r   website_i18n_runtimer   r    r!   r"   r#   r$   __file__resolver  r  rO  r*  rT  r  rN  rU  r  r<  rE  r  rg  r  r  r'   __annotations__r7   r;   rB   r*  POKEMON_NEWS_CACHE_MINUTESr@  r0  r  r  rG   r  DEFAULT_SESSION_TTL_SECONDSDEFAULT_TOKEN_TTL_SECONDSr   r}   r  rg   rq   r   r  rW   rb   rc   r   r   r   rY   r   r^  r   r  r   r   r  rd   r  r  r  r  r  r   r   r   r   r   r   r   r   r   r  r  r  r  r!  r0  r:  rA  rG  rI  rQ  rU  rW  rb  re  rh  rn  ru  rz  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r7  r:  r  r>  rE  rP  rV  rb  rf  rr  ry  r{  r}  r  r  r  r=  r  
SystemExitru   rh   rf   <module>rT     s   5 5 5 " " " " " "      				 				   % % % % % %       ' ' ' ' ' ' ' '       E E E E E E E E       " " " " " " " " ! ! ! ! ! ! ! ! ! ! ! !       R R R R R R R R U  U  U  U  U  U  U  U  U  U  U  U  U  U  U  U K  K  K  K  K  K  K  K  K  K  K  K G G G G G G G G p  p  p  p  p  p  p  p  p  p  p  p  p  p  p  ptH~~'*       &&	%%d++++	++	#&@@ '   y) , , ,DFF	DFF $&& $&&DFF TVV &( ( ( ( ("! !
     # #     $ $      (* %!"  #  &(  ( ( ( (( ) 7     	 	 	 2 2 2 2 2   5 5 5 5 	/66IBI0"5566<<>>S#:B??@@FFHH c)")$6;;<<BBDD $}T*<*S@SUUc#ss929-H##NiJjJj#k#k  $K  pK   L   L  M  M CSS+I33OhKiKi!j!j  "H  oH  I  I  J  J 3yry!:B??@@FFHH %%ibi0G&O&OPPC		"7<<==CCEEKKMM IBI3R8899??AA((3JB)O)OPP  2 2 2 2E E E EH H H H< < < <L L L L# # # #L   :
' 
' 
' 
'            	 	 	 	          ^ ^ ^ ^             3 3 3 3: : : :0 0 0 0       "
0 
0 
0 
0       
& & & & &0 0 0 00 0 0 0 @ @ @ @ @ @$
 
 
 
	Z 	Z 	Z 	Z :1 	,. ,. ,. ,. ,. ,.^" " " "
    6 6 6 6"    # # # #
	 	 	 	 	7 7 7 70    &    >
 
 
 
H H H H HV0 0 0 06 6 6 6Y Y Y Y
 >X 
B 
B 
B 
B 
B FKdi      0, , , , ,^
[ 
[ 
[ 
[ 
[
 
 
 
 
 ]{ . . . . .6   &   < < < <
 
 
 
<   Fg Fg Fg Fg Fg& Fg Fg FgRU U U Up z
*TTVV

 rh   