Ë
    ªÅjUa  ã                   ó>	  — d 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ZdZdZdZdZdZdZd	Zd
ZdZdZej.                  j1                  d«      r ej2                  dej4                  «      rdnej.                  j7                  d«      ZdZ ej<                   ej>                  d¬«      «      Z dddœZ!g d¢Z"g d¢Z# ejH                  dejJ                  «      Z& ejH                  dejJ                  «      Z'g d¢Z( ejH                  dejJ                  «      Z) ejH                  d«      Z* ejH                  d«      Z+ ejH                  dejJ                  «      Z, ejH                  d«      Z- ejH                  d «      d!f ejH                  d"«      d!f ejH                  d#«      d!f ejH                  d$«      d!f ejH                  d%ejJ                  «      d!fgZ. ejH                  d&ejJ                  «      Z/ ejH                  d'ejJ                  «      Z0d(„ Z1d)„ Z2d*„ Z3d+„ Z4dqd,„Z5d-diZ6d.„ Z7drd/„Z8da9d0„ Z:dsd1„Z;dsd2„Z<ddefd3„Z=d4„ Z>dtd5„Z?drd6„Z@d7diZAd8d9d:œZBd;ZCdud<„ZDdvd=„ZEd>„ ZFdwd?„ZGd@„ ZHdxdA„ZIdydB„ZJdzdC„ZKdD„ ZLd{dE„ZMeNdFk(  �rddlOZO eOj                   dG¬H«      ZQeQj¥                  dI«       eQj¥                  dJeSdK¬L«       eQj¥                  dMeTdN¬O«       eQj¥                  dPeTdQ¬L«       eQj¥                  dRdSdT¬U«       eQj«                  «       ZV ej                  «       ZWeVj–                  rÕ eKeVj°                  eVj–                  ¬«      D ]³  ZYej                  jµ                   eT ej’                  dVeYdI   «      j·                  dN«      «      e «      Z\eYdW   rdWndXZ] e^dYe\dZ›d[eYd\   d]›d^eYd_   d]›d`eYda   d]›dbe]› dc�«       eYdd   dde D ]3  Z_ ejÀ                  dfd! e4e_«      «      ddg Za e^dh e3e_«      › diea› �«       Œ5 Œµ y eIeVj°                  eVjÄ                  reWeVjÄ                  djz  z
  ndeVjÆ                  ¬k«      Zd e^ eMed«      «       eVjÊ                  rBeddl   D ]9  Z_ ejÀ                  dfd! e4e_«      «      ddm Za e^dne_do   › dp e3e_«      › diea› �«       Œ; yyy)|a!  xsafe: the ONE way every tool and every Claude session calls the X API (twitterapi.io).

Ben, 3 Oct 2026: "Build one tool that excludes all scam links, bots and scam call channels, that all
sessions and all tools use forever, so we don't pay for them and don't count them. Legit callers are fine."

What it does
  1. build_query()  adds exclusion operators to every search, so X never returns (and we never pay for)
                    vote farms, claim/airdrop phishing, whale-buy and trending bot feeds, known bot accounts.
                    Keeps the whole query under 500 characters: twitterapi.io silently returns ZERO posts
                    above ~512 characters (measured 3 Oct 2026), which looks exactly like "nobody posts".
  2. classify()     second pass on every returned post. 'hard' level = scam / campaign / bot feed / blocked
                    account (safe for any tool). 'full' level adds templates, coin lists, no-human-words
                    (for sentiment and mood work).
  3. search(), window(), timeline()   credit guard, cache, per 4h windows with exact or estimated rates.
  4. Legit callers are NOT spam: posting about many coins is never a reason. Only what the text does
     (phishing, vote farming, copy-paste templates, bot feeds) gets an account blocked.

Mood is never scored with keywords here. real posts are returned so a reader judges the feeling.

Key: env TWAPI_KEY, else /opt/common/twitterapi_key, else ~/.twapi_key. Never printed.
Public copy: http://159.223.88.168/hx-c9ois/fw/xsafe.py  (store: .../xsafe_store.json)
é    Nz¤1.9.1 (3 Oct 2026, gold-set check: 14 verified real posts were removed by 1.7/1.8; fixed zero-width, coin list, copytrading, leaderboard, long-form false positives)é   g       @g…ëQ¸…Û?z4http://159.223.88.168/hx-c9ois/fw/xsafe_version.jsonz*http://159.223.88.168/hx-c9ois/fw/xsafe.pyzhttps://api.twitterapi.ioiô  iPÃ  é–   z/opt/commonz/opt/common/xsafez~/.xsafez2http://159.223.88.168/hx-c9ois/fw/xsafe_store.jsoné   )Úhoursz"Mozilla/5.0 (Macintosh) Chrome/128zapplication/json)z
User-AgentÚAccept)!z"whale just bought"z"Listing ID"Úleaderboardz"vote matters"z"votes are needed"z"PAID DEXScreener"z"Community Vote"z"private TG"z
"DEX PAID"z"new gainer"z"WHALE BUY"z"just got bought"z"TRENDING on"z"votes needed"z"alpha group"z"votes to go"Úgiveawayz"claim your"z"eligibility checker"z"allocation portal"z"wallet trail"z"reward status"z"dump risk"z"allocation tool"z"reward page"z"wallet checker"z"holder page"z"time to claim"z"token record"z"connect wallet"z"reward drop"z"Daily Report"z"VIP group")ÚLongLaunchesÚwhalewatchRHÚanubisgtradeÚMintDetector1ÚALaunchhunterÚtopcontractÚMoontokListingÚTrenchWatchXaâ  \bclaim(s|ing)? (your|now|here|live|is live|it now)|time to claim|opened up claims|airdrop (is )?(live|campaign|checker|claim)|claim (the |your )?airdrop|airdrop.{0,40}(connect|check your|eligib)|eligib\w* (checker|check|wallets?)|check (your )?eligib|wallets? (qualif\w*|is eligible)|allocations? (tool|portal|is live|for (robinhood|solana) users)|check(ing)? (the )?allocation|(allocation|claim|reward|airdrop) portal|portal is (now |officially )?live|connect (your )?(web3 |metamask )?wallet|link your wallet|wallet checker|giveaway|dm (me|for) (for )?(access|the next|early|calls?|promo)|promo code|(join|buy) (the )?presale|presale (is )?live|free (bag|airdrop)|free tokens? to claim|check (your |ur )?allocation|sign up for a free|reward (page|status|drop|lookup)|guaranteed .{0,20}reward|holder (page|lookup|status)|wallet trail|token record|address (review|records?|history|tool)|scan id|dump risk|check (it )?out on #?gmgn|check out this (crypto )?airdrop|claim(ed)? mine|got a claim|has a claim|holders? (might want to|can|should) check|check (your |ur )?wallet|wallet check|eligibility (is )?show|on the portal|holders? (are )?getting (something|smth)|holders? actually got|got smth|if you faded|just claimed|claims? (is |are )?(live|open)aµ  \bvotes? (needed|are needed|matters|counts|to go|makes the difference)|listing id|leaderboard.{0,40}vote|vote.{0,40}leaderboard|vote dashboard|community vote\b|paid dexscreener|dex ?paid|new gainer|whale (just )?(bought|buy)|just got bought|whale alert|buy alert|volume alert|new pair (alert|detected|created)|token alert|trending on|top \d+ gainers|daily report|t\.me/|join (our|my) (private )?(tg|telegram|alpha|vip|group)|in my (private )?(alpha )?(tg|telegram|group)|my tg buys|call channel|calls? group|vip group|alpha group|private (alpha|group|tg)|copy ?trade (me|my)|signal group|timeline &(amp;)? audit|alpha spotted|making waves|(from|in) my (tg|telegram)( channel)?|telegram channel)ÚrecordÚlookupzlook upÚtrailÚstatusÚhistoryÚaddresszwallet datazon-chainÚcheckÚreviewÚoutcomeÚresultÚshareÚbalancesÚpageÚqueryÚreceiptsÚtraceÚeligibleÚrewardaq  portal is (active|open|live)|takes \d+ ?(sec|seconds|min)|boosted|ticket id|listing request|has reached \d+(\.\d+)?x|since being surfaced|surfaced by|from my call|tg frens|is performing [+-]?\d|just did \d+(\.\d+)?x|early call|\d+(\.\d+)?x (gain )?from my|trench scanner|join the effort|community followers|trending alert|call-?outs have done|watch this video to changeu   [â€‹-â€�â� ï»¿]u   [a-zA-Z][Ð€-Ó¿]|[Ð€-Ó¿][a-zA-Z]zU\bclaim\b|claim (now|here|your)|airdrop (live|is live|claim)|portal|allocation|eligibzO\$[A-Za-z][A-Za-z0-9]{1,11}\b|0x[0-9a-fA-F]{40}|\b[1-9A-HJ-NP-Za-km-z]{32,44}\bzhttps?://\S+ú z\b[1-9A-HJ-NP-Za-km-z]{32,44}\bz0x[0-9a-fA-F]{40}z[$#@]\w+z\d[\d,.]*[kmb%x]?zödrop (your|ur) (sol|solana|eth|evm|base|wallet|address)|\bi'?ll be giving|\b(giving|give) away \d|(first|1st) \d[\d,]* (people|users|followers|wallets).{0,60}(follow|retweet|\brt\b|tollow)|lost wallets?.{0,40}claim|on incinerator|telegram is herez±(kept telling you|told you|i gave you|begged you) to buy|al diye yalvard|\d+x yapt|it did \d+(\.\d+)?x|first to call|callout hit \d|\bi called\b.{0,40}\d+(\.\d+)?x|\bnext call\bc                 ó²   — | j                  «       } t        D ]  \  }}|j                  || «      } Œ t        j                  dd| «      j	                  «       d d S )Nú\s+r$   éZ   )ÚlowerÚMASKÚsubÚreÚstrip)ÚxÚrÚss      ú)/opt/launchwatch/pub/hx-c9ois/fw/xsafe.pyÚnormr1   i   sM   € Ø	�‰‹	€AÜÒ%‰ˆˆ1˜!Ÿ%™%  1›+‘aÐ%Ü�6‰6�&˜#˜qÓ!×'Ñ'Ó)¨#¨2Ð.Ð.ó    c                 óæ   — | j                  «       }t        D ]  \  }}|j                  ||«      }Œ t        t	        j
                  d|«      «      t        t	        j
                  d|«      «      dz  z   S )Nz
[a-z']{2,}u   [ã�€-é¿¿ê°€-íž¯]é   )r(   r)   r*   Úlenr+   Úfindall)r-   Únr.   r*   s       r0   Únwordsr8   o   s]   € Ø	�‰‹	€AÜÒ)‰ˆˆ3˜AŸE™E # q›M™Ð)ÜŒr�z‰z˜-¨Ó+Ó,¬s´2·:±:Ð>QÐSTÓ3UÓ/VÐZ[Ñ/[Ñ[Ð[r2   c                 ó˜   — | j                  d«      xs i }|j                  d«      xs | j                  d«      xs dj                  «       S )NÚauthorÚuserNameÚaÚ )Úgetr(   )Útr<   s     r0   Ú_authorr@   u   s>   € Ø	�‰ˆh‹Ò˜2€AØ�E‰E�*ÓÒ1 §¡ s£Ò1¨r×8Ñ8Ó:Ð:r2   c                 ój   — | j                  d«      �| j                  d«      S | j                  dd«      S )NÚtextr-   r=   )r>   ©r?   s    r0   Ú_textrD   z   s,   € ØŸE™E &›MÐ5ˆ1�5‰5�‹=ÐI¸1¿5¹5ÀÀb»>ÐIr2   c           	      óò  — |xs t        j                   «       }| D �cg c]  }t        ||«      rŒ|‘Œ }}|D �cg c]  }|t        |«      z
  t        dz  k\  sŒ|‘Œ }}|D �cg c]  }|t        |«      z
  t        dz  k  sŒ|‘Œ }}t	        d„ |D «       «      }t        |«      dk\  r|t        |«      z  nt        }t        |t        |«      |z  z   |t        |«      t        |d«      ¬«      S c c}w c c}w c c}w )z¹the ONE count to compare windows across time (see COMPARABLE_LIKES). Returns dict(count, settled, young,
    share). count = settled real posts with 3+ likes + young real posts x share.é  c              3   óT   K  — | ]   }|j                  d «      xs dt        k\  –— Œ" y­w)Ú	likeCountr   N)r>   ÚCOMPARABLE_LIKES)Ú.0r?   s     r0   ú	<genexpr>zcomparable.<locals>.<genexpr>…   s$   è ø€ ÒMÀˆq�u‰u�[Ó!Ò& QÔ+;Õ;ÑMùs   ‚&(é
   r   )ÚcountÚsettledÚyoungr   )	ÚtimeÚclassifyÚtsÚSETTLE_HÚsumr5   ÚDEFAULT_SHAREÚdictÚround)	ÚpostsÚnowÚlevelr?   ÚrealrN   rO   Úhitr   s	            r0   Ú
comparabler]   ~   sß   € ð Ò
”—‘“€CØÖ7�!¤H¨Q°Õ$6ŠAÐ7€DÐ7ØÖA�Q #¬¨1«¡+´¸D±Ó"@ŠqÐA€GÐAØÖ>�1 ¤b¨£e¡¬h¸©oÓ =ŠQÐ>€EÐ>Ü
ÑMÀWÔMÓ
M€CÜ"% g£,°"Ò"4ˆC”#�g“,Ò¼-€EÜ�cœC ›J¨Ñ.Ñ.¸Ä3ÀuÃ:ÔUZÐ[`ÐbcÓUdÔeÐeùò 8ùÚAùÚ>s!   �C*¯C*¹C/ÁC/Á!C4Á?C4r?   c                  óž   — t        j                  t        t        d«      j	                  «       «      j                  «       S # t        $ r Y y w xY w)NÚrb)ÚhashlibÚmd5ÚopenÚ__file__ÚreadÚ	hexdigestÚ	Exception© r2   r0   Ú_own_md5rh   �   s7   € Ü—‘œD¤¨4Ó0×5Ñ5Ó7Ó8×BÑBÓDÐ	DøÜÒ!™TÐ!ús   �>A  Á 	AÁAc                 óø  — t        j                   «       t        d   z
  | k  rt        S t        j                   «       t        d<   	 t        j                  t
        j                  j                  t
        j                  j                  t        t        ¬«      d¬«      «      }|j                  d«      r|d   t        «       k(  rt        S 	 t
        j                  j                  t
        j                  j                  t        t        ¬«      d¬«      j                  «       }t!        j"                  |«      j%                  «       |d   k7  rt        S 	 t'        j(                  t*        t&        j,                  «      r4t*        j/                  d«      st1        t*        d«      j3                  |«       t5        «       }d	D �ci c]  }||v sŒ|||   “Œ }}t7        t9        |t*        d
«      |«       |j;                  |«       t        j                   «       |d   d<   t<        j>                  j3                  d|d   › d�«       t5        «       d   S # t        $ r	 t        cY S w xY w# t        $ r Y Œ»w xY wc c}w # t        $ r Y ŒAw xY w)a)  every run uses the latest filter (Ben, 3 Oct 2026). Checks the published version at most every 10 min.
    If this copy is older, downloads the latest, checks its md5, overwrites this file when writable and reloads
    the code in place, so callers keep working on the new rules without a restart.r?   ©ÚheadersrL   ©Útimeoutra   é   z/opt/common/Úwb)Ú_STOREÚ_SPENTÚ_CFGÚ_LATESTÚexecrs   zxsafe: updated to ÚVERSIONú
) rP   rs   ru   ÚjsonÚloadÚurllibÚrequestÚurlopenÚRequestÚVERSION_URLÚUArf   r>   rh   ÚSRC_URLrd   r`   ra   re   ÚosÚaccessrc   ÚW_OKÚ
startswithrb   ÚwriteÚglobalsrt   ÚcompileÚupdateÚsysÚstderr)Úmax_ageÚvÚsrcÚgÚkÚkeeps         r0   Úensure_latestr�   ’   sè  € ô ‡y�yƒ{”W˜S‘\Ñ! GÒ+´G¨^Ü—9‘9“;„GˆC�LðÜ�I‰I”f—n‘n×,Ñ,¬V¯^©^×-CÑ-CÄKÔY[Ð-CÓ-\ÐfhÐ,ÓiÓjˆð �5‰5�Œ<˜1˜U™8¤x£zÒ1¼'°>ðÜ�n‰n×$Ñ$¤V§^¡^×%;Ñ%;¼GÌRÐ%;Ó%PÐZ\Ð$Ó]×bÑbÓdˆÜ�;‰;�sÓ×%Ñ%Ó'¨1¨U©8Ò3¼G°^ð	Ü�y‰yœ¤2§7¡7Ô+´H×4GÑ4GÈÔ4WÜ”X˜tÓ$×*Ñ*¨3Ô/ä‹IˆÐ0WÖb¨1Ð[\Ð`aÒ[a˜q ! A¡$™wÐb�tÐbÜŒW�Sœ( FÓ+¨QÔ/°·±¸$´ÔUY×U^ÑU^ÓU`ÀÀ9ÁÈcÑARÜ�
‰
×ÑÐ-¨a°	©l¨^¸2Ð>Ô?ô ‹9�YÑÐøô ò ÜŠðûô Ò™$ÐüÚbøô ò Ùðús^   ÁAI ÃBI- ÅAI Æ)I- Æ7	I(ÇI(Ç	A.I- ÉIÉIÉ	I%É"I- É$I%É%I- É-	I9É8I9c                  óº  — t         �t         S t        j                  j                  t        d«      } d}	 t        j                  j                  | «      rUt        j                  «       t        j                  j                  | «      z
  dk  rt        j                  t        | «      «      }|€—	 t        j                  t        j                  j                  t        j                  j                  t         t"        ¬«      d¬«      «      }t        j$                  t        d¬«       t        j&                  |t        | d	«      «       g }|j)                  d
g «      D ]5  }|j+                  t-        j.                  |t,        j0                  «      «       Œ7 t3        |t5        |j)                  dg «      «      t5        d„ |j)                  dg «      D «       «      t5        d„ |j)                  dg «      D «       «      ¬«      a t         S # t        $ r d}Y �Œaw xY w# t        $ r4 t        j                  t        | «      «      }n# t        $ r i }Y nw xY wY �Œw xY w# t        $ r Y �Œw xY w)zatemplates (normalized text seen on 3+ coins) and blocked accounts (repeat scam/campaign posters).Nz
store.jsoni`T  rj   é   rl   T©Úexist_okÚwÚpatternsÚ	templatesc              3   ó<   K  — | ]  }|j                  «       –— Œ y ­w©N©r(   ©rJ   r<   s     r0   rK   zstore.<locals>.<genexpr>Ã   s   è ø€ ÒS|ÐbcÐTU×T[ÑT[×T]ÑS|ùó   ‚Úblockedc              3   ó<   K  — | ]  }|j                  «       –— Œ y ­wr™   rš   r›   s     r0   rK   zstore.<locals>.<genexpr>Ä   s   è ø€ ÒB¨!˜AŸG™GŸIÑBùrœ   Úallow)r–   r—   r�   rŸ   )rp   r€   ÚpathÚjoinÚHOMEÚexistsrP   Úgetmtimerw   rx   rb   rf   ry   rz   r{   r|   Ú	STORE_URLr~   ÚmakedirsÚdumpr>   Úappendr+   r†   ÚIrV   Úset)ÚpÚdÚpatsr-   s       r0   Ústorer®   ¯   sÀ  € ô Ð¤&˜=Ü
�‰�‰”T˜<Ó(€AØ€AðÜ�7‰7�>‰>˜!Ô¤§¡£¬r¯w©w×/?Ñ/?ÀÓ/BÑ!BÀXÒ!MÔSW×S\ÑS\Ô]aÐbcÓ]dÓSeÈqà€yð	%Ü—	‘	œ&Ÿ.™.×0Ñ0´·±×1GÑ1GÌ	Ô[]Ð1GÓ1^ÐhjÐ0ÓkÓlˆAÜ�K‰Kœ tÕ,¬d¯i©i¸¼4ÀÀ3»<Ô.Hð €DØ�U‰U�:˜rÓ"ò ˆØ�[‰[œŸ™ A¤r§t¡tÓ,Õ-ðô ˜4¬3¨q¯u©u°[À"Ó/EÓ+FÔPSÑS|Ðgh×glÑglÐmvÐxzÓg{ÔS|ÓP|ÜÑB¨q¯u©u°W¸bÓ/AÔBÓBôD€Fä€Møô Ò˜$“aÐûô
 ò 	%Ü—Y‘Yœt A›wÓ'‘øÜÒ$ "šaÐ$ýð	%ûô Òš$Ðús[   ´A4G; Â+BH Å3IÇ;H
È	H
È	I
ÈH5È4I
È5IÉ I
ÉIÉI
É	I
É	IÉIc                 ó.  ‡‡— t        | «      xs dŠt        | «      }t        «       }||d   v ryt        j	                  ‰«      ryt        ‰«      dk\  }t        j	                  ‰«      r$t        j	                  ‰«      rt        ‰«      dk  ry|st        j	                  ‰«      ryt        j                  d	‰t        j                  «      ry
|st        j	                  ‰«      ry
‰j                  «       Št        ˆfd„t        D «       «      dk\  rt        j                  d‰«      sy||d   v s|d„ t        D «       v ry|st         j	                  ‰«      ryt#        t$        j'                  ‰«      «      dk\  ry|dk(  ryt)        ‰«      |d   v ryt+        ˆfd„|j-                  dg «      D «       «      ryt#        t/        t0        j'                  ‰«      «      «      }|dk\  rt        ‰«      d|z  k  ryt        ‰«      dk  ry|st2        j	                  ‰«      ryy) zgNone = a real person. Else the reason. level 'hard' is safe for any tool; 'full' is for sentiment work.r=   rŸ   Nzscam (look-alike letters)r   éP   zscam (hidden letters)Úscamz3votes? (needed|are needed|matters|to go)|listing idzcampaign or bot feedc              3   ó&   •K  — | ]  }|‰v –— Œ
 y ­wr™   rg   )rJ   r•   Úlows     €r0   rK   zclassify.<locals>.<genexpr>Ó   s   øè ø€ Ò
)˜ˆ1�Œ8Ñ
)ùs   ƒé   zE\b(buy|sell|sold|bought|pump|dump|chart|mc|cap|bag|moon|bottom|top)\bzscam (phishing prose)r�   c              3   ó<   K  — | ]  }|j                  «       –— Œ y ­wr™   rš   )rJ   r-   s     r0   rK   zclassify.<locals>.<genexpr>Õ   s   è ø€ Ò!D° !§'¡'§)Ñ!Dùrœ   zblocked accountzgiveaway farmr4   zcall channel brag listÚhardr—   Útemplatec              3   ó@   •K  — | ]  }|j                  ‰«      –— Œ y ­wr™   )Úsearch)rJ   r.   Úraws     €r0   rK   zclassify.<locals>.<genexpr>Ú   s   øè ø€ Ò
8˜Qˆ1�8‰8�C�=Ñ
8ùs   ƒr–   zlearned patterné   g      ø?z	coin listzno human wordszbot feed)rD   r@   r®   Ú	LOOKALIKEr¹   r8   Ú	HIDDEN_ZWÚZW_SCAMWORDSÚSCAMr+   r©   ÚCAMPAIGNr(   rT   ÚPHISH_WORDSÚEXCL_FROM_SEEDÚGIVEAWAYr5   ÚBRAGr6   r1   Úanyr>   rª   ÚCOINREFÚBOTFEED2)r?   rZ   r<   ÚSÚ	long_formÚrefsr³   rº   s         @@r0   rQ   rQ   È   s´  ù€ ä
�‹(Š.�b€Cœg a›j˜!¬e«g¨!ØˆAˆg‰J�˜tÜ×Ñ˜ÔÐ%@Ü�s“˜sÑ"€IÜ×Ñ˜Ô¤×!4Ñ!4°SÔ!9¼fÀS»kÈBÒ>NÐWnÙœŸ™ SÔ)°&Ü	‡y�yÐGÈÌbÏdÉdÔSÐ\rÙœŸ™¨Ô-Ð6LØ
�)‰)‹+€CÜ
Ó
)œ[Ô
)Ó)¨QÒ.´r·y±yð  BJð  LOô  8PØ&ØˆAˆi‰LÑ˜AÑ!D´^Ô!DÑDÐM^ÙœŸ™¨Ô-°oÜ
Œ4�<‰<˜ÓÓ Ò"Ð+CØ�‚˜tÜˆCƒy�A�k‘NÑ"¨:Ü
Ó
8 !§%¡%¨
°BÓ"7Ô
8Ô8ÐARÜŒs”7—?‘? 3Ó'Ó(Ó)€DØˆq‚y”V˜C“[ 3¨¡:Ò-°kÜˆcƒ{�Q‚Ð/ÙœŸ™¨Ô-°jØr2   c                 ó€   — g g }}| D ]2  }t        ||«      }|r|n|j                  |s|nt        ||¬«      «       Œ4 ||fS )N)Ú_why)rQ   r¨   rV   )rX   rZ   r[   Úspamr?   r.   s         r0   ÚsplitrÎ   â   sP   € Ø�Rˆ$€DØò FˆÜ�Q˜ÓˆÙ‰˜×$Ñ$©a¡Q´T¸!À!´_ÕEðFð �ˆ:Ðr2   c                 ó¬  — d| v r#| j                  dd«      j                  «       } d}nÔd}|rdt        |«      › �nd|rdt        |«      › �ndz   }|t        | «      z
  t        |«      z
  dz
  }t        t
        D �cg c]  }d|z   ‘Œ	 c}z   D ]j  }d|z   }	|j                  d«      d	   j                  d
«      j                  «       | j                  «       v rŒJt        |«      t        |	«      z   |kD  r n||	z  }Œl | |z   } |r| dt        |«      › �z  } |r| dt        |«      › �z  } t        | «      t        dz   kD  rt        dt        | «      › d�«      ‚| S c c}w )zaq plus exclusions, never longer than MAXQ. 'xsafe:off' in q = raw query (only for scam research).z	xsafe:offr=   z since_time:z until_time:é   zfrom:z -ú:éÿÿÿÿú"rL   zquery z0 chars: twitterapi.io returns 0 posts above ~512)
Úreplacer,   Úintr5   Ú
EXCL_TERMSrÂ   rÎ   r(   ÚMAXQÚ
ValueError)
ÚqÚsinceÚuntilÚroomÚexÚtailÚbudgetÚfÚtermÚpieces
             r0   Úbuild_queryrã   ê   s]  € à�aÑØ�I‰I�k 2Ó&×,Ñ,Ó.ˆ°R±àˆÙ/4�,œs 5›z˜lÑ+¸"Ñ`eÀ<ÔPSÐTYÓPZÈ|ÑA\ÐkmÑnˆØœ˜A›‘¤ T£Ñ*¨QÑ.ˆÜ´~Ö!F°! '¨A£+Ò!FÑFò 	ˆDØ˜4‘KˆEØ�z‰z˜#‹˜rÑ"×(Ñ(¨Ó-×3Ñ3Ó5¸¿¹»ÑBÀHÜ�2‹wœ˜U›Ñ# fÒ,©eØ�%‰K‰Bð		ð
 �‰FˆÙˆa�\¤# e£* Ð.Ñ.ˆaÙˆa�\¤# e£* Ð.Ñ.ˆaÜ
ˆ1ƒv”�r‘	Ò¤¨f´S¸³V°HÐ<lÐ,mÓ!nÐnØ€Hùò "Gs   Á9Ec                  ó  — t         j                  j                  d«      } dt         j                  j	                  d«      fD ]-  }| r n)t        |«      j                  «       j                  «       } Œ/ | st        d«      ‚| S # t        $ r Y ŒJw xY w)NÚ	TWAPI_KEYz/opt/common/twitterapi_keyz~/.twapi_keyzBno twitterapi.io key (env TWAPI_KEY or /opt/common/twitterapi_key))
r€   Úenvironr>   r    Ú
expanduserrb   rd   r,   rf   ÚRuntimeError)rŽ   r«   s     r0   Ú_keyré   ÿ   sx   € Ü
�
‰
�‰�{Ó#€AØ*¬B¯G©G×,>Ñ,>¸~Ó,NÐOò ˆÙ‰eÜ�a“—‘“×%Ñ%Ó'‰Qðñ ”LÐ!eÓfÐfØ€Høô Ò™$Ðús   Á'B Â 	BÂBc           	      óì  — t         | z   |r"dt        j                  j                  |«      z   ndz   }t	        |«      D ]s  }	 t        j
                  j                  t        j
                  j                  |dt        «       it        ¥¬«      d¬«      5 }t        j                  |«      cd d d «       c S  t        d«      ‚# 1 sw Y   nxY wŒŽ# t        j                  j                  $ rf}|j                  dv r2t        d|j                  › d	|j                  d
k(  rd› �«      ‚d› �«      ‚t!        j"                  dd|z  z   «       Y d }~�Œd }~wt$        $ r t!        j"                  dd|z  z   «       Y �Œ6w xY w)Nú?r=   z	X-API-Keyrj   é(   rl   )i�  i‘  é’  i“  i”  zX API ú: rí   z
no creditsÚrefusedr   r»   r4   zX API unreachable)ÚAPIry   ÚparseÚ	urlencodeÚrangerz   r{   r|   ré   r~   rw   rx   ÚerrorÚ	HTTPErrorÚcoderè   rP   Úsleeprf   )r    ÚparamsÚtriesÚuÚir.   Úes          r0   Ú_getrý   	  so  € Üˆd‰
¹f�cœFŸL™L×2Ñ2°6Ó:Ò:È"ÑM€AÜ�5‹\ò "ˆð	"Ü—‘×'Ñ'¬¯©×(>Ñ(>¸qÈ;ÔX\ÓX^ÐJeÔbdÐJeÐ(>Ó(fÐprÐ'Ósð $ÐwxÜ—y‘y “|÷$ó $ð"ô Ð*Ó
+Ð+÷$ð $úð $øä�|‰|×%Ñ%ò 	"Ø�v‰vÐ2Ñ2¼,ÈÐPQ×PVÑPVÈxÐWYÐjk×jpÑjpÐtwÒjwÐZfð  ZHð  HIó  ;Jð  5Jð  ~Gð  ZHð  HIó  ;Jð  5JÜ�J‰J�q˜1˜q™5‘y×!Ò!ûÜò 	"Ü�J‰J�q˜1˜q™5‘y×!ð	"ús8   Á AC
ÂB=Â%	C
Â=C	ÃC
Ã
E3Ã'AEÅ'E3Å2E3c                 ó  — t         j                  j                  t        d«      }	 t	        j
                  t        |«      «      }t        j                  «       |d   z
  | k  r|d   S 	 t        dd «      }|j                  d«      xs d|j                  d«      xs dz   }t        j                  t        d¬	«       t	        j                  t        j                  «       |d
œt        |d«      «       |S # t        $ r Y Œ”w xY w# t        $ r Y |S w xY w)Nzbal.jsonr?   Úcz/oapi/my/infoÚrecharge_creditsr   Útotal_bonus_creditsTr“   )r?   rÿ   r•   )r€   r    r¡   r¢   rw   rx   rb   rP   rf   rý   r>   r¦   r§   )rŠ   r«   r¬   rÿ   s       r0   Úcreditsr    sÜ   € Ü
�‰�‰”T˜:Ó&€AðÜ�I‰I”d˜1“gÓˆÜ�9‰9‹;˜˜3™Ñ 'Ò)°!°C±&¨=Ð)äˆ_˜dÓ#€AØ	
�‰Ð!Ó	"Ò	' a¨A¯E©EÐ2GÓ,HÒ,MÈAÑN€AÜ	�‰”T DÕ	)¬4¯9©9¼4¿9¹9»;ÈQÑ5OÔQUÐVWÐY\ÓQ]Ô+^à€Høô Ò‘dÐûô Ò�dØ€Hð ús$   ¦?C0 ÂAC? Ã0	C<Ã;C<Ã?	DÄDÚpagesé<   F)ÚcapÚbulkz.http://159.223.88.168/hx-c9ois/fw/xstatus.jsonc                 óL   — t        | «      t        d<   t        |«      t        d<   y)zhdeclare a bigger job: max pages this run. Bulk jobs stop by themselves when the shared burn brake is on.r  r  N)rÕ   rr   Úbool)r  r  s     r0   rß   rß   )  s   € ä�e“*„Dˆ�K¬T°$«Zœd 6šlr2   c                 ój  — t         j                  j                  t        d«      }	 t	        j                  «       t         j                  j                  |«      z
  | k  rt        j                  t        |«      «      S 	 	 t        j                  t        j                  j                  t        j                  j                  t        t        ¬«      d¬«      «      }t        j                   t        d¬«       t        j"                  |t        |d«      «       |S # t        $ r Y Œ¤w xY w# t        $ r i cY S w xY w)zcshared live balance + burn rate for ALL callers of the key (published every minute by the droplet).zstatus.jsonrj   rL   rl   Tr“   r•   )r€   r    r¡   r¢   rP   r¤   rw   rx   rb   rf   ry   rz   r{   r|   Ú
STATUS_URLr~   r¦   r§   )rŠ   r«   r¬   s      r0   r   r   .  sØ   € ä
�‰�‰”T˜=Ó)€AðÜ�9‰9‹;œŸ™×)Ñ)¨!Ó,Ñ,¨wÒ6¼t¿y¹yÌÈaËÓ?QÐ8QÐ6ðÜ�I‰I”f—n‘n×,Ñ,¬V¯^©^×-CÑ-CÄJÔXZÐ-CÓ-[ÐegÐ,ÓhÓiˆÜ
�‰”D 4Õ(¬$¯)©)°A´t¸A¸s³|Ô*DÈQÀhøô Ò‘dÐûô ò ØŠ	ðús%   ¦AD Á=BD$ Ä	D!Ä D!Ä$D2Ä1D2c                  ó®  — t        «       } | r¥t        j                  «       | j                  dd«      z
  dk  r{| j                  d«      �/| d   | j                  dt        «      k  rt	        d| d   d›d�«      ‚t
        d	   r1| j                  d
«      rt	        d| j                  d«      d›d�«      ‚y y t        «       t        k  rt	        dt        › d�«      ‚y )Nr?   r   éX  ÚbalanceÚfloorz
X credits ú,z% under floor: no X calls until top upr  ÚbrakezX burn brake ON (Úburn_per_min_10mz2 credits/min across all callers): bulk job stoppedzX credits under z*: refusing to spend (top up twitterapi.io))r   rP   r>   ÚFLOORrè   rr   r  )Ústs    r0   Ú_guardr  ;  sÖ   € Ü	‹€BÙ	Œd�i‰i‹k˜BŸF™F 3¨›NÑ*¨SÒ0Ø�6‰6�)ÓÐ(¨R°	©]¸R¿V¹VÀGÌUÓ=SÒ-SÜ ¨B¨y©M¸!Ð+<Ð<aÐbÓcÐcÜ�Š<˜BŸF™F 7œOÜÐ!2°2·6±6Ð:LÓ3MÈaÐ2Pð  QCð   Dó  Eð  Eð ,ˆ<ä	‹”UÒ	ÜÐ-¬e¨WÐ4^Ð_Ó`Ð`ð 
r2   c                 óL  — t        j                  | › d|› �j                  «       «      j                  «       }t        j
                  j                  t        d|d d |dz   «      }t        j                  d| «      }t        |«      xr2 t        |j                  d«      «      t        j                  «       dz
  k  }	 |dk\  rvt        j
                  j                  |«      rW|s7t        j                  «       t        j
                  j                  |«      z
  |k  rt!        j"                  t%        |«      «      S t(        d	   t*        d
   k\  r(t-        dt*        d
   › dt*        d
   t.        z  d›d�«      ‚t(        d	   dz  dk(  r
t1        «        d| dœ}|r||d<   t3        d|«      }t(        d	xx   dz  cc<   t	        j4                  t        j
                  j7                  |«      d¬«       t!        j8                  |t%        |d«      «       |S # t&        $ r Y ŒÞw xY w# t&        $ r Y |S w xY w)Nú|Úcacher4   z.jsonúuntil_time:(\d+)rÐ   i0*  r   r  r  zxsafe run cap reached (z	 pages, ~r  z1 credits). Call xsafe.budget(n) for a bigger job.rL   ÚLatest)Ú	queryTyper   Úcursorz/twitter/tweet/advanced_searchTr“   r•   )r`   Úsha1Úencodere   r€   r    r¡   r¢   r+   r¹   r  rÕ   ÚgrouprP   r£   r¤   rw   rx   rb   rf   rq   rr   rè   ÚCOST_PER_PAGEr  rý   r¦   Údirnamer§   )	r   r  Ú	cache_ttlÚkeyÚcpÚmÚfrozenrø   r¬   s	            r0   Ú_pager&  F  sç  € Ü
�,‰,˜%˜  & Ð*×1Ñ1Ó3Ó
4×
>Ñ
>Ó
@€CÜ	�‰�‰”d˜G S¨¨! W¨c°G©mÓ	<€BÜ
�	‰	Ð% uÓ-€AÜ�!‹WÒAœ˜QŸW™W Q›Z›¬4¯9©9«;¸Ñ+AÑA€FðØ˜Š>œbŸg™gŸn™n¨RÔ0±fÄÇ	Á	ÃÌbÏgÉg×N^ÑN^Ð_aÓNbÑ@bÐenÒ@nÔx|÷  yBñ  yBô  CGð  HJó  CKó  yLð  rLäˆg�œ$˜u™+Ò%ÜÐ4´T¸%±[°MÀÌ4ÐPUÉ;ÔYfÑKfÐghÐJið  j[ð  \ó  ]ð  	]Üˆg�˜Ñ˜qÒ ¤&¤(Ø#¨eÑ4€FÙ &ˆv�hÑÜÐ-¨vÓ6€A¼¸w»È1Ñ8L»Ü	�‰”R—W‘W—_‘_ RÓ(°4Õ	8¼$¿)¹)ÀAÄtÈBÐPSÃ}Ô:Uà€Høô Ò‘dÐûô Ò�dØ€Hð ús&   Â9A:H Æ1AH È	HÈHÈ	H#È"H#c                 ód   — t         j                  j                  | d   d«      j                  «       S )NÚ	createdAtz%a %b %d %H:%M:%S %z %Y)ÚdtÚdatetimeÚstrptimeÚ	timestamprC   s    r0   rR   rR   Y  s'   € Ü�;‰;×Ñ  +¡Ð0IÓJ×TÑTÓVÐVr2   c                 ój  — t        «        t        | ||«      }g dddf\  }}}	}
t        «       }t        t	        d|«      «      D ]¤  }t        |||«      }|	dz  }	|j                  d«      xs g D ]G  }|j                  d«      |vsŒ|j                  |j                  d«      «       |j                  |«       ŒI t        |j                  d«      «      }
|j                  d«      }|
r|rŒ¤ n |s_t        j                  d	«       t        |dd
«      }|	dz  }	t        |j                  d«      xs g «      }t        |j                  d«      «      }
t        ||«      \  }}t        |«       |D �cg c]  }t        |«      ‘Œ }}t!        |||||	|
|
 |rt#        |«      nd|rt	        |«      ¬«	      S d¬«	      S c c}w )zxreturns dict(posts, real, spam, n_pages, has_more, oldest, newest, query). Spam is already mostly excluded by the query.Nr   FrÐ   ÚtweetsÚidÚhas_next_pageÚnext_cursorr4   rÒ   )	r   rX   r[   rÍ   Ún_pagesÚhas_moreÚcompleteÚoldestÚnewest)r�   rã   rª   ró   Úmaxr&  r>   Úaddr¨   r  rP   r÷   ÚlistrÎ   Ú_strikesrR   rV   Úmin)rÙ   rÚ   rÛ   r  rZ   r!  r   rX   Úcurr7   ÚmoreÚseenÚ_r¬   r?   r[   rÍ   Útts                     r0   r¹   r¹   ]  sœ  € ä„OÜ˜˜5 %Ó(€EØ˜d A uÐ,Ñ€Eˆ3��4Ü‹5€DÜ”3�q˜%“=Ó!ò &ˆÜ�%˜˜iÓ(ˆ¨!¨q©&¨!Ø—‘�x“Ò& Bò 	OˆAØ�u‰u�T‹{ $Ò&¨¯©°·±°t³Ô(=¸u¿|¹|ÈA½ð	Oä�A—E‘E˜/Ó*Ó+ˆ°1·5±5¸Ó3G¨SÙš3¡ð&ñ ä�
‰
�1Œœ5 ¨¨bÓ1�q°1¸±6°1Ü�Q—U‘U˜8“_Ò*¨Ó+ˆ´D¸¿¹¸Ó9OÓ4P¨TÜ�u˜eÓ$�J€Dˆ$ÜˆT„NØÖ	�AŒ"ˆQ�%Ð	€BÐ	ô �e 5¨t¸$ÈÐTXÐgkÐckÙ"$”s˜2”w¨$Á"´s¸2³wôPð PØJNôPð Pùò 
 s   Å(F0c                 óH  — t        | |||¬«      }||z
  dz  }|d   r|d   rt        ||d   z
  dz  d«      }d}n|}d}|j                  |||t        |d   «      |z  t        |d	   «      |z  t        |d
   «      |z  t	        |d   «      t	        |d   «      d   |z  ¬«       |S )zÕposts per hour in [since, until). Exact when the window fits in the pages read, else estimated from the
    covered part (the newest posts). Rates: all returned posts (spam excluded by query) and real people only.)r  rF   r3  r5  gš™™™™™©?FTrX   r[   rÍ   rM   )r   Ú	covered_hÚexactÚrate_allÚ	rate_realÚ	rate_spamr]   Úrate_comparable)r¹   r7  r‡   r5   r]   )rÙ   rÚ   rÛ   Ú	max_pagesr.   ÚhrsÚcovrC  s           r0   ÚwindowrK  v  sÌ   € ô 	ˆq�%˜ iÔ0€AØ�5‰=˜DÑ
 €CØˆ‚}˜˜8šÜ�5˜1˜X™;Ñ&¨$Ñ.°Ó5ˆ¸u±uàˆ˜4�5Ø‡H�H�3 #¨UÜ˜!˜G™*“o¨Ñ+´s¸1¸V¹9³~ÈÑ7KÔWZÐ[\Ð]cÑ[dÓWeÐhkÑWkÜ" 1 W¡:Ó.Ä
È1ÈWÉ:Ó@VÐW^Ñ@_ÐbeÑ@eð ô gð €Hr2   c           	      óâ   — t        |xs t        j                  «       «      }g }t        ||z  dd«      D ]8  }|||z  dz  z
  ||dz
  |z  dz  z
  }}|j                  t	        | |||«      «       Œ: |S )zaper-step windows ending at `end` (default now), oldest first. One page per window (~150 credits).r   rÒ   rF   rÐ   )rÕ   rP   ró   r¨   rK  )	rÙ   Úendr   ÚsteprH  ÚoutrŽ   r<   Úbs	            r0   ÚtimelinerQ  …  s}   € ä
ˆcÒ ”T—Y‘Y“[Ó
!€C¨ 3Ü�5˜D‘= ! RÓ(ò /ˆØ�Q˜‘X ‘_Ñ$ c¨Q°©U°d©N¸TÑ,AÑ&Aˆ1ˆØ�
‰
”6˜!˜Q  9Ó-Õ.ð/ð €Jr2   c                 ó  — 	 | D �cg c]c  }|j                  dd«      j                  d«      r@t        j                  t	        |«      |d   t        t        j                  «       «      dœ«      ‘Œe }}|rvt        j                  t        d¬«       t        t        j                  j                  t        d«      d«      5 }|j                  d	j                  |«      d	z   «       d
d
d
«       y
y
c c}w # 1 sw Y   y
xY w# t        $ r Y y
w xY w)z›repeat scam/campaign posters become blocked accounts (built hourly by xsafe_learn.py). Content only:
    a caller who posts many coins never gets a strike.rÌ   r=   )r±   Úcampaign)r<   r•   r?   Tr“   zstrikes.jsonlr<   rv   N)r>   rƒ   rw   Údumpsr@   rÕ   rP   r€   r¦   r¢   rb   r    r¡   r„   rf   )rÍ   r?   Úlinesrà   s       r0   r:  r:  Ž  sÕ   € ðØ_cö HÐZ[Ø—E‘E˜& "Ó%×0Ñ0Ð1EÔFô —‘¤'¨!£*°1°V±9Ä3ÄtÇyÁyÃ{ÓCSÑTÕUð Hˆð HáÜ�K‰Kœ tÕ,Ü”b—g‘g—l‘l¤4¨Ó9¸3Ó?ÐfÀ1ÀaÇgÁgÈdÏiÉiÐX]ÓN^ÐaeÑNeÔFf×fÐfð ùòH÷ gÐfûÜÒ‘dÐúsB   ‚C: †A(C)Á.AC: Â;$C.ÃC: Ã)C: Ã.C7Ã3C: Ã7C: Ã:	DÄDc                 ó†  — d„ }dt        | d   «      › dt        | d   «      › d| d   › �g}| d   d | D ]|  }|j                  d«      xs i }t        j                  d	d
t	        |«      «      d d }|j                  d ||«      › d|j                  d«      › d|j                  dd«      › d|› �«       Œ~ dj                  |«      S )Nc                 óz   — t         j                  j                  t        | «      t        «      j                  d«      S )Nú%d %b %H:%M)r)  r*  ÚfromtimestamprR   ÚBKKÚstrftimerC   s    r0   ú<lambda>zshow.<locals>.<lambda>›  s&   € ”R—[‘[×.Ñ.¬r°!«u´cÓ:×CÑCÀMÓR€ r2   zreal people r[   z  | spam left after query filter rÍ   z	 | pages r2  r:   r&   r$   éÜ   z  z @r;   z (Ú	followersrë   z followers): rv   )r5   r>   r+   r*   rD   r¨   r¡   )r.   r7   ÚwhenrU  r?   r<   Úbodys          r0   Úshowra  š  sÞ   € ÙR€DØœC  &¡	›NÐ+Ð+KÌCÐPQÐRXÑPYËNÐK[Ð[dÐefÐgpÑeqÐdrÐsÐt€EØˆv‰Y�r˜ˆ]ò hˆØ�E‰E�(‹OÒ!˜rˆÜ�v‰v�f˜c¤5¨£8Ó,¨T¨cÐ2ˆØ�‰�r™$˜q›'˜ " Q§U¡U¨:Ó%6Ð$7°r¸!¿%¹%ÀÈSÓ:QÐ9RÐR_Ð`dÐ_eÐfÕgðhð �9‰9�UÓÐr2   Ú__main__z2xsafe: X search without scams, bots and vote farms)Údescriptionr   z--hourszlook back N hours)ÚtypeÚhelpz--pagesrÐ   )rd  Údefaultz
--timelinezper-4h windows over N hoursz--whyÚ
store_truez'also list removed posts with the reason)Úactionre  r  rC  Ú	estimatedzwindow ending rX  z BKK: comparable rG  z.2fz/h, real people rE  z/h, all rD  z/h (ú)r[   é   r&   é    z    @rî   rF   )rÚ   r  rÍ   éx   z  [removed: rÌ   z] @)NÚfull)r  )rn  )r   )T)r  )Né,  )NNrL   rn  ro  )rL   )Né   r»   rL   )é   )fÚ__doc__r`   rw   r€   r+   rˆ   rP   Úurllib.parsery   Úurllib.requestÚurllib.errorr*  r)  ru   rI   rS   rU   r}   r   rð   r×   r  r  r    Úisdirr�   r‚   rç   r¢   r¥   ÚtimezoneÚ	timedeltarZ  r~   rÖ   rÂ   r†   r©   r¿   rÀ   rÁ   rÇ   r½   r¼   r¾   rÆ   r)   rÃ   rÄ   r1   r8   r@   rD   r]   rs   rh   r�   rp   r®   rQ   rÎ   rã   ré   rý   r  rq   rr   r
  rß   r   r  r&  rR   r¹   rK  rQ  r:  ra  Ú__name__ÚargparseÚArgumentParserÚapÚadd_argumentÚfloatrÕ   Ú
parse_argsr<   rY   r   r•   rY  r  rM  ÚkindÚprintr?   r*   r`  r   r  r.   Úwhyrg   r2   r0   ú<module>rƒ     s’  ðñ÷, T× S× S× SÓ SÛ ð q€ð Ð Ø€Ø€ØD€Ø
6€Ø!€Ø
€Ø€Ø€Ø Ÿg™gŸm™m¨MÔ:¸y¸r¿y¹yÈÐXZ×X_ÑX_Ô?`ÑÐfh×fmÑfm×fxÑfxð  zDó  gE€Ø@€	Ø€b‡k�k�,�"—,‘, QÔ'Ó(€Ø8ÐDVÑW€òf€
òC€ð €r‡z�zð Wð Y[×X\ÑX\ó^€ð ˆ2�:‰:ð dð
 fh÷  fjñ  fjók€ò%€ð ˆ2�:‰:ð hð jl÷  jnñ  jnóo€ð ˆB�J‰JÐ4Ó5€	ØˆB�J‰JÐHÓI€	Øˆr�z‰zÐrÐtv×txÑtxÓy€Ø
ˆ"�*‰*ÐgÓ
h€Ø	ˆ�‰�OÓ	$ cÐ*¨Z¨R¯Z©ZÐ8ZÓ-[Ð]`Ð,aØ	ˆ�‰Ð(Ó	)¨3Ð/°*°"·*±*¸[Ó2IÈ3Ð1OØ	ˆ�‰Ð(¨"¯$©$Ó	/°Ð5ð7€ð ˆ2�:‰:ð SàTV×TXÑTXóZ€ð €r‡z�zð `Øac×aeÑaeóg€ò/ò\ò;ò
Jó	fð �ˆ(€ò"ó
 ð4 
€òó2ó4ð  T°ó ò*ó,ó
ð �1ˆ€Ø˜5Ñ!€Ø=€
ó8ó

òaóò&WóPó2óò	óð ˆzÓÛØ	 ˆ×	 Ñ	 Ð-aÔ	b€BØ‡O�O�GÔ˜bŸo™o¨i¸eÐJ]˜oÔ^Ø‡O�O�I C°€OÔ3°R·_±_À\ÐX[Ðb°_ô  6AØ‡O�O�G LÐ7`€OÔaØ
�‰‹€AØ
ˆ$�)‰)‹+€CØ‡z‚zÙ˜!Ÿ'™'¨¯©Ô4ò 	4ˆAØ—+‘+×+Ñ+©C°	°·	±	Ð:MÈqÐQXÉzÓ0Z×0`Ñ0`ÐabÓ0cÓ,dÐfiÓjˆCØ šj‘7¨kˆDÙ�N 3 {Ð"3Ð3DÀQÐGXÑEYÐZ]ÐD^Ð^nÐopÐq|Ño}ð  Bð  oCð  CKð  LMð  NXñ  LYð  Z]ð  K^ð  ^bð  cgð  bhð  hið  jô  kØ�v‘Y˜r �]ò 4�Ø�r—v‘v˜f c©5°«8Ó4°T°cÐ:�Ù˜™g a›j˜\¨¨D¨6Ð2Õ3ñ4ñ		4ñ �1—7‘7¸!¿'º' #¨¯©°$©Ò"6ÀtÐST×SZÑSZÔ[ˆÙ‰d�1‹gŒØ�5Š5Ø�v‘Yò I�Ø�r—v‘v˜f c©5°«8Ó4°T°cÐ:�Ù˜ Q v¡Y K¨s±7¸1³:°,¸bÀÀÐGÕHñIð ð' r2   