ó
    v´jI  ã                  óÊ  • S r SSKJr  SSKrSSKrSSKrSSKrSSKrSSKr	SSK
r	SSKJrJr  SSKJr  SSKJrJr  SSKJr  SrS	rS
rSrSrSrSrSr\S-  rSrSrSr \" SS9 " S S5      5       r!\" SS9 " S S5      5       r"\" SS9 " S S5      5       r# " S S\$5      r%S:S jr&S;S jr'S<S  jr( S=         S>S! jjr)S=S?S" jjr*S@S# jr+ S=         SAS$ jjr,SBS% jr-S=SCS& jjr.S=SDS' jjr/SES( jr0SFS) jr1S=SGS* jjr2SHS+ jr3SIS, jr4SJS- jr5 S=             SKS. jjr6SLS/ jr7SMS0 jr8SNS1 jr9S2r:S3S4.                 SOS5 jjr;SPS6 jr<SQS7 jr=SRS8 jr>SSS9 jr?g)Ta  The hosted playground's sign-in through WorkOS, and the session cookie it
leaves behind.

Two programs have to agree on this: the app's login gate, and the wake
function that stands in for the app while no task is running. Whichever of
the two the load balancer routes a request to has to be able to complete a
sign-in, so both serve the same three routes from the functions here, and
the app accepts a cookie the function set without a second sign-in. The wake
function's package is built from this file and its own, so everything here
is standard library only, the call to WorkOS included.

WorkOS proves who a person is and which organization they belong to. The
playground decides who may enter: only members of the one organization named
in settings. The WorkOS access token is read once, when the sign-in
completes, and thrown away; what the playground keeps is its own cookie.

A cookie value and a sign-in ``state`` share one format: a JSON record,
base64url encoded, a dot, and an HMAC-SHA256 over the encoded record under
the session secret. The record's ``kind`` keeps the two apart, so neither can
stand in for the other. A cookie's expiry is fixed when it is issued; using
the playground does not extend it. When it lapses while the WorkOS session is
still alive, the person goes to WorkOS and straight back without typing
anything.

``/login`` answers with a page rather than a bare redirect. A link scraper
never signs in, so the page a pasted link unfurls from is this one, and it
carries the preview tags; a browser moves on to WorkOS at once.
é    )ÚannotationsN)ÚasdictÚ	dataclass)Úescape)ÚAnyÚOptional)Ú	urlencodeÚquber_sessionzhttps://playground.qubera.aiz/static/link-card.pngi°  iv  u    Qubera â€” extraction playgroundz~Upload a financial filing and ask a question. Every figure in the answer is cited to the page and the table cell it came from.é   i  i„  zhttps://api.workos.comé   T)Úfrozenc                  óB   • \ rS rSr% SrS\S'   S\S'   S\S'   S\S'   Srg	)
ÚWorkOSéE   z¿What both programs need to run a sign-in: the WorkOS environment's API
key and client ID, the one organization whose members may enter, and the
secret that signs the cookie and the ``state``.ÚstrÚapi_keyÚ	client_idÚorganization_idÚsession_secret© N©Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__annotations__Ú__static_attributes__r   ó    Ú6/home/mande/repo/quber/src/quber/playground/session.pyr   r   E   s   ‡ ñ7ð ƒLØƒNØÓØÖr   r   c                  óL   • \ rS rSr% SrS\S'   S\S'   S\S'   S\S'   S\S'   S	rg
)ÚIdentityéQ   z‹Who a session belongs to, as WorkOS reported it at sign-in. ``sid`` is
the WorkOS session id, which sign-out needs to end that session too.r   Úuser_idÚemailr   ÚroleÚsidr   Nr   r   r   r    r"   r"   Q   s$   ‡ ñLð ƒLØƒJØÓØ
ƒIØ	†Hr   r"   c                  ó\   • \ rS rSr% SrS\S'   SrS\S'   SrS\S'   SrS\S	'   Sr	S
\S'   Sr
g)ÚSignIné]   zŠHow a return from WorkOS ends. Accepted: a redirect to the page the
person asked for, with a cookie. Refused or failed: a page, no cookie.ÚintÚstatusNúOptional[str]ÚlocationÚtokenÚpageúOptional[Identity]Úidentityr   )r   r   r   r   r   r   r.   r/   r0   r2   r   r   r   r    r)   r)   ]   s8   ‡ ñNð ƒKØ"€HˆmÓ"Ø€Eˆ=ÓØ€Dˆ-ÓØ#'€HÐ Ö'r   r)   c                  ó   • \ rS rSrSrSrg)ÚSignInErroréi   z#WorkOS did not confirm the sign-in.r   N)r   r   r   r   r   r   r   r   r    r4   r4   i   s   † Ü-r   r4   c                ó¢   • [         R                  " U R                  5       UR                  5       [        R                  5      R                  5       $ ©N)ÚhmacÚnewÚencodeÚhashlibÚsha256Ú	hexdigest)ÚsecretÚbodys     r    Ú	signaturer@   m   s.   € Ü�8Š8�F—M‘M“O T§[¡[£]´G·N±NÓC×MÑMÓOÐOr   c                óT   • [         R                  " U S[        U 5      * S-  -  -   5      $ )NÚ=é   )Úbase64Úurlsafe_b64decodeÚlen)Útexts    r    Ú	b64decoderH   q   s'   € Ü×#Ò# D¨3´3°t³9°*¸q±.Ñ+AÑ$AÓBÐBr   c                óÊ   • [         R                  " [        R                  " USS9R	                  5       5      R                  5       R                  S5      nU S[        X5       3$ )N)Ú,Ú:)Ú
separatorsrB   Ú.)rD   Úurlsafe_b64encodeÚjsonÚdumpsr:   ÚdecodeÚrstripr@   )r>   Úrecordr?   s      r    r:   r:   u   sR   € Ü×#Ò#¤D§J¢J¨vÀ*Ñ$M×$TÑ$TÓ$VÓW×^Ñ^Ó`×gÑgÐhkÓl€DØˆV�1”Y˜vÓ,Ð-Ð.Ð.r   c                óð  • U(       a  UR                  S5      S:w  a  gUR                  S5      u  pE[        R                  " [	        X5      U5      (       d  g [
        R                  " [        U5      5      n[        U[        5      (       a  UR                  S5      U:w  a  gUR                  S5      n[        U[        5      (       a  Xsb  UO[        R                  " 5       ::  a  gU$ ! [         a     gf = f)z´The record inside ``token`` when its signature holds, it is of ``kind``,
and it has not expired. ``now=None`` checks against the clock; pass
``float("-inf")`` to ignore the expiry.rM   é   NÚkindÚexpires)ÚcountÚsplitr8   Úcompare_digestr@   rO   ÚloadsrH   Ú
ValueErrorÚ
isinstanceÚdictÚgetr+   Útime)r>   r/   rV   Únowr?   ÚgivenrS   rW   s           r    rQ   rQ   z   sÆ   € ö �E—K‘K Ó$¨Ó)ØØ—+‘+˜cÓ"�K€DÜ×Òœy¨Ó6¸×>Ñ>ØðÜ—’œI d›OÓ,ˆô �fœd×#Ñ# v§z¡z°&Ó'9¸TÓ'AØØ�j‰j˜Ó#€GÜ�gœs×#Ñ# w¹/±3ÌtÏyÊyË{Ó'[ØØ€Møô ó Ùðús   ÁC( Ã(
C5Ã4C5c                ó�   • [        Ub  UO[        R                  " 5       5      [        -   n[        U SS0[	        U5      ESU0E5      $ )NrV   ÚsessionrW   )r+   r`   ÚSESSION_SECONDSr:   r   )r>   r2   ra   rW   s       r    Úissuerf   ‘   s@   € Ü˜™‘#¬d¯iªi«kÓ:¼_ÑL€GÜ�&˜6 9ÐU´°xÓ0@ÐUÀ)ÈWÑUÓVÐVr   c           
     ó¾   • U c  g  [        [        U S   5      [        U S   5      [        U S   5      [        U S   5      [        U S   5      S9$ ! [         a     g f = f)Nr$   r%   r   r&   r'   ©r$   r%   r   r&   r'   )r"   r   ÚKeyError)rS   s    r    Úidentity_ofrj   –   so   € Ø�~Øð	ÜÜ˜˜yÑ)Ó*Ü�f˜W‘oÓ&Ü Ð'8Ñ 9Ó:Ü�V˜F‘^Ó$Ü�F˜5‘MÓ"ñ
ð 	
øô ó Ùðús   †AA Á
AÁAc                ó†   • [        [        XSU5      5      nUb&  [        R                  " UR                  U5      (       d  gU$ )z’The identity in a live session cookie for ``organization_id``, or None.
A cookie issued under another organization is refused like an expired one.rd   N)rj   rQ   r8   rZ   r   )r>   r   r/   ra   r2   s        r    Úverifyrl   ¥   s=   € ô
 œ6 &°¸CÓ@ÓA€HØÑœt×2Ò2°8×3KÑ3KÈ_×]Ñ]ØØ€Or   c           
     ó@   • [        [        XS[        S5      5      5      $ )z»The identity in a cookie whose signature holds, expired or not. Sign-out
reads the WorkOS session id this way, so a person whose cookie has just
lapsed can still end their WorkOS session.rd   z-inf)rj   rQ   Úfloat)r>   r/   s     r    Úsigned_identityro   °   s   € ô ”v˜f¨Y¼¸f»ÓFÓGÐGr   c                óˆ   • [        Ub  UO[        R                  " 5       5      [        -   n[        U S[	        U5      US.5      $ )NÚstate)rV   ÚnextrW   )r+   r`   ÚSTATE_SECONDSr:   Ú	safe_next)r>   Ú	next_pathra   rW   s       r    Ú	state_forrv   ·   s7   € Ü˜™‘#¬d¯iªi«kÓ:¼]ÑJ€GÜ�& 7´I¸iÓ4HÐU\Ñ]Ó^Ð^r   c                ól   • [        XSU5      nUc  g[        [        UR                  SS5      5      5      $ )zHThe return page a ``state`` carries, or None when it is forged or stale.rq   Nrr   Ú )rQ   rt   r   r_   )r>   rq   ra   rS   s       r    Únext_from_statery   ¼   s3   € ä�F 7¨CÓ0€FØ�~ØÜ”S˜Ÿ™ F¨BÓ/Ó0Ó1Ð1r   c                óz   • U (       a4  U R                  S5      (       a  U R                  S5      (       d  SU ;  a  U $ g)zHThe page to land on after sign-in: a path on this site, never elsewhere.Ú/z//Ú\)Ú
startswith)Úraws    r    rt   rt   Ä   s0   € æ
ˆs�~‰~˜c×"Ñ"¨3¯>©>¸$×+?Ñ+?ÀDÐPSÃOØˆ
Ør   c                ó   • U  SU 3$ )zÔThe site a request came in on. The callback and the sign-out return
both hang off it, so the hosted playground and a developer machine each
get their own; WorkOS accepts only the ones registered in its dashboard.z://r   )ÚschemeÚhosts     r    Úbase_urlr‚   Ë   s   € ð ˆX�S˜˜ÐÐr   c                ó|   • [        U R                  US-   SS[        U R                  X#5      S.5      n[         SU 3$ )Nz	/callbackÚcodeÚauthkit)r   Úredirect_uriÚresponse_typeÚproviderrq   z/user_management/authorize?)r	   r   rv   r   Ú
WORKOS_API)ÚconfigÚbaseru   ra   Úquerys        r    Úauthorize_urlr�   Ò   sL   € Üà×)Ñ)Ø  ;Ñ.Ø#Ø!Ü˜v×4Ñ4°iÓEñ	
ó€Eô ˆ\Ð4°U°GÐ<Ð<r   c                ó8   • [        XS-   S.5      n[         SU 3$ )z�Where to send a browser to end its WorkOS session. WorkOS then returns it
to ``base``, which has to be a registered sign-out URI.r{   )Ú
session_idÚ	return_toz!/user_management/sessions/logout?)r	   r‰   )r'   r‹   rŒ   s      r    Ú
logout_urlr‘   ß   s'   € ô  S¸c±zÑBÓC€EÜˆ\Ð:¸5¸'ÐBÐBr   c                ó  • [         R                  " U R                  U R                  SUS.5      R	                  5       n[
        R                  R                  [         S3USSS.SS9n [
        R                  R                  U[        S9 n[         R                  " UR                  5       5      nS	S	S	5        [+        W[,        5      (       d  [!        S5      eU$ ! , (       d  f       N0= f! [
        R                  R                   aA  nUR                  5       R                  S
S9S	S n[!        SUR"                   SU 35      UeS	nAf[
        R                  R$                  [&        [(        4 a  n[!        SU 35      UeS	nAff = f)z:Trade the code WorkOS sent back for the person's identity.Úauthorization_code)r   Úclient_secretÚ
grant_typer„   z/user_management/authenticatezapplication/json)zContent-TypeÚAcceptÚPOST)ÚdataÚheadersÚmethod)ÚtimeoutNÚreplace)Úerrorsi,  zWorkOS answered z: zWorkOS could not be reached: z3WorkOS answered with something other than an object)rO   rP   r   r   r:   ÚurllibÚrequestÚRequestr‰   ÚurlopenÚWORKOS_TIMEOUT_SECONDSr[   ÚreadÚerrorÚ	HTTPErrorrQ   r4   r„   ÚURLErrorÚTimeoutErrorr\   r]   r^   )rŠ   r„   r?   rŸ   ÚresponseÚanswerÚexcÚdetails           r    Úauthenticater¬   æ   s[  € ä�:Š:à×)Ñ)Ø#Ÿ^™^Ø.Øñ		
ó÷ �fƒhð 	ô �n‰n×$Ñ$Üˆ,Ð3Ð4ØØ!3Ð?QÑRØð	 %ð €GðJÜ�^‰^×#Ñ# GÔ5KÐ#ÑLÐPXÜ—Z’Z §¡£Ó0ˆF÷ Mô �fœd×#Ñ#ÜÐOÓPÐPØ€M÷ MÕLûä�<‰<×!Ñ!ó LØ—‘“×"Ñ"¨)Ð"Ð4°T°cÐ:ˆÜÐ,¨S¯X©X¨J°b¸¸ÐAÓBÈÐKûÜ�L‰L×!Ñ!¤<´Ð<ó JÜÐ9¸#¸Ð?Ó@ÀcÐIûðJúsB   Á)"C+ Â%CÂ0C+ Ã
C(Ã$C+ Ã(C+ Ã+FÄ	<EÅ,FÅ1F Æ Fc                óâ   • U R                  S5      n[        U5      S:w  a  0 $  [        R                  " [	        US   5      5      n[        U[        5      (       a  U$ 0 $ ! [
         a    0 s $ f = f)z°The claims inside a WorkOS access token, read without checking its
signature. That is safe here only because the token arrived straight from
WorkOS over HTTPS in the same call.rM   é   rU   )rY   rF   rO   r[   rH   r\   r]   r^   )Úaccess_tokenÚpartsÚfounds      r    Úclaimsr²     so   € ð ×Ñ˜sÓ#€EÜ
ˆ5ƒz�QƒØˆ	ðÜ—
’
œ9 U¨1¡XÓ.Ó/ˆô ˜u¤d×+Ñ+ˆ5Ð3°Ð3øô ó ØŠ	ðús   ¤"A ÁA.Á-A.c                ó.  • [        U R                  X55      nUb  U(       d  [        S[        U5      S9$  [	        X5      nUR                  S5      n[        U[        5      (       a  UO0 n	[        [        UR                  SS5      5      5      n
[        U	R                  SS5      5      n[        U
R                  SS5      5      nUR                  S5      U R                  :w  a(  U(       a  [        XÁ5      OS	n[        S
[        X½U5      S9$ [        [        U	R                  SS5      5      UU R                  [        U
R                  SS5      5      US9n[        SU[        U R                  Xå5      US9$ ! [
         a    [        S[        U5      S9s $ f = f)au  Finish a sign-in WorkOS sent back to ``/callback``.

The organization check happens here: an account outside the configured
organization is refused, and gets no cookie. WorkOS has already signed
that account in by then, so the refusal page offers to end the WorkOS
session; otherwise the next sign-in would come straight back as the same
account until WorkOS times it out.
i�  ©r0   Úuserr¯   rx   r%   r'   r   ú/logini“  Úidr&   rh   i/  )r.   r/   r2   )ry   r   r)   Úfailed_pager¬   r4   r_   r]   r^   r²   r   r   r‘   Úrefusal_pager"   rf   )rŠ   r‹   r„   rq   Úmark_srcra   Útargetr©   Úraw_userrµ   Útoken_claimsr%   r'   Úswitchr2   s                  r    Úcomplete_sign_inr¿     sf  € ô  ˜V×2Ñ2°EÓ?€FØ�~žTÜ�c¤¨HÓ 5Ñ6Ð6ð7Ü˜fÓ+ˆð �z‰z˜&Ó!€HÜ'1°(¼D×'AÑ'A™8Àr€DÜœ#˜fŸj™j¨¸Ó<Ó=Ó>€LÜ�—‘˜ "Ó%Ó&€EÜ
ˆl×Ñ˜u bÓ)Ó
*€CØ‡z�zÐ#Ó$¨×(>Ñ(>Ó>Þ*-”˜CÔ&°8ˆÜ�c¤¨U¸HÓ EÑFÐFÜÜ�D—H‘H˜T 2Ó&Ó'ØØ×.Ñ.Ü�×!Ñ! &¨"Ó-Ó.Øñ€Hô �# ¬e°F×4IÑ4IÈ8Ó.YÐdlÑmÐmøô# ó 7Ü�c¤¨HÓ 5Ñ6Ò6ð7ús   µE4 Å4FÆFc               ó    • [        U [        US9$ )zžThe Set-Cookie value for a session, as the app and the wake function
both send it: HttpOnly, SameSite=Lax, and Secure on HTTPS, which is every
hosted request.©Úsecure)Úcookie_valuere   )r/   rÂ   s     r    Úcookie_headerrÄ   :  s   € ô ˜œ°vÑ>Ð>r   c                ó   • [        SSU S9$ )Nrx   r   rÁ   )rÃ   rÁ   s    r    Úclear_cookie_headerrÆ   A  s   € Ü˜˜A fÑ-Ð-r   c               óz   • [          SU  3SU 3SSS/nU(       a  UR                  S5        SR                  U5      $ )NrB   zMax-Age=zPath=/ÚHttpOnlyzSameSite=LaxÚSecurez; )ÚCOOKIEÚappendÚjoin)r/   Úmax_agerÂ   r°   s       r    rÃ   rÃ   E  sB   € Üˆx�q˜˜Ð  H¨W¨IÐ"6¸À*ÈnÐ]€EÞØ�‰�XÔØ�9‰9�UÓÐr   aA
  <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title}</title>
{head}
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&amp;family=IBM+Plex+Sans:wght@400;600&amp;family=IBM+Plex+Serif:wght@400;600&amp;display=swap" />
<style>
  /* Values follow the playground's shared token set. */
  * {{ box-sizing: border-box; }}
  body {{ margin: 0; min-height: 100vh; display: grid; place-items: center; padding: 24px 16px;
         background: #f5f8fb; color: #15222e;
         font-family: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
         -webkit-font-smoothing: antialiased; }}
  main {{ width: min(420px, 100%); padding: 36px; background: #ffffff; border: 1px solid #e2e8ef;
          border-radius: 14px; box-shadow: 0 4px 14px rgba(12, 32, 50, .08), 0 18px 50px rgba(12, 32, 50, .10);
          display: flex; flex-direction: column; gap: 18px; }}
  .brand {{ display: flex; align-items: center; gap: 12px; }}
  .brand img {{ width: 32px; height: 32px; display: block; }}
  .brand span {{ font-family: "IBM Plex Serif", Georgia, serif; font-weight: 600; font-size: 22px;
                 letter-spacing: -0.01em; }}
  .copy {{ display: flex; flex-direction: column; gap: 10px; }}
  .eyebrow {{ margin: 0; font-family: "IBM Plex Mono", ui-monospace, monospace; font-size: 11px;
              letter-spacing: 0.15em; text-transform: uppercase; color: #74828f; }}
  h1 {{ margin: 0; font-family: "IBM Plex Serif", Georgia, serif; font-weight: 400; font-size: 26px;
        line-height: 1.15; letter-spacing: -0.015em; }}
  p.message {{ margin: 0; font-size: 15px; line-height: 1.6; color: #475563; overflow-wrap: anywhere; }}
  a.action {{ align-self: flex-start; min-height: 44px; padding: 0 18px; display: inline-flex; align-items: center;
              border-radius: 999px; background: #1E4E78; color: #ffffff; font-size: 14px; font-weight: 600;
              text-decoration: none; }}
  a.action:hover {{ background: #163b5c; }}
  a.action:focus-visible {{ outline: none; box-shadow: 0 0 0 3px rgba(191, 139, 58, .25); }}
</style>
</head>
<body>
<main>
  <div class="brand"><img src="{mark}" alt="" /><span>Qubera</span></div>
  <div class="copy">
    <p class="eyebrow">{eyebrow}</p>
    <h1>{heading}</h1>
    <p class="message">{message}</p>
  </div>
  <a class="action" href="{href}">{action}</a>
</main>
</body>
</html>
rx   )Úheadc                ó°   • [         R                  [        U 5      U[        USS9[        U5      [        U5      [        U5      [        USS9[        U5      S9$ )a  One card on the paper background, with the Qubera mark and wordmark,
in the design system's type and colors. Self-contained apart from the
fonts and ``mark_src``, because the wake function serves it while nothing
under the app's /static/ answers; each program passes a mark it can serve.T©Úquote)ÚtitlerÎ   ÚmarkÚeyebrowÚheadingÚmessageÚhrefÚaction)Ú_PAGEÚformatr   ©rÒ   rÔ   rÕ   rÖ   rØ   r×   rº   rÎ   s           r    r0   r0   €  sT   € ô �<‰<Ü�U‹mØÜ�H DÑ)Ü�w“Ü�w“Ü�w“Ü�D Ñ%Ü�f‹~ð ð 	ð 	r   c                ór   • [        XU5      nS[        USS9 S3n[        SSSSS	UU[        5       S
-   U-   S9$ )zpThe page ``/login`` answers with: preview tags for link scrapers, and an
immediate move to WorkOS for a browser.z+<meta http-equiv="refresh" content="0; url=TrÐ   ú" />u   Qubera â€” sign inúSign inzTaking you to sign inzMThe playground is for invited members. Your browser should move on by itself.zContinue to sign inÚ
rÛ   )r�   r   r0   Úlink_preview_tags)rŠ   r‹   ru   rº   r»   Úrefreshs         r    Ú
login_pagerâ   ›  sZ   € ô ˜6¨Ó3€FØ;¼FÀ6ÐQUÑ<VÐ;WÐW[Ð\€GÜØ"ØØ'Ø_Ø$ØØÜÓ  4Ñ'¨'Ñ1ñ	ð 	r   c           
     ó<   • U =(       d    Sn[        SSSU S3SUUS9$ )NzThis accountu   Qubera â€” no accessÚAccessu(   This account canâ€™t open the playgroundzF is not a member of the Qubera organization. Only members can sign in.z Sign in with a different account©rÒ   rÔ   rÕ   rÖ   rØ   r×   rº   r´   )r%   Úswitch_hrefrº   Úwhos       r    r¹   r¹   ¬  s6   € Ø
×
!�>€CÜØ$ØØ:Ø�%Ð]Ð^Ø1ØØñð r   c           
     ó    • [        SSSSSSU S9$ )Nu!   Qubera â€” sign-in did not finishrÞ   u   The sign-in didnâ€™t finishzMIt could not be confirmed, or it took longer than 15 minutes. Start it again.zSign in againr¶   rå   r´   )rº   s    r    r¸   r¸   ¹  s$   € ÜØ1ØØ-Ø_ØØØñð r   c                 ó:  • [         [        -   n [        [        SS9n[        [        SS9nSSU4SSSS[         S	-   4SS
U4SSU4SSU 4SSU 4SSS[        [        5      4SS[        [        5      4SSU4SSSU4SSU4SSU 4SSU4/nSR                  S U 5       5      $ )zFThe Open Graph and Twitter Card tags, one per line, for a page's head.TrÐ   ÚnameÚdescription)Úpropertyzog:typeÚwebsite)rì   zog:site_nameÚQuberarì   zog:urlr{   zog:titlezog:descriptionzog:imagezog:image:secure_url)rì   zog:image:typez	image/pngzog:image:widthzog:image:heightzog:image:alt)rê   ztwitter:cardÚsummary_large_imageztwitter:titleztwitter:descriptionztwitter:imageztwitter:image:altrß   c              3  ó@   #   • U  H  u  pnS U SU SU S3v •  M     g7f)z<meta z="z" content="rÝ   Nr   )Ú.0ÚattrÚkeyÚvalues       r    Ú	<genexpr>Ú$link_preview_tags.<locals>.<genexpr>Ý  s-   é € Ð_ÒZ^ÑFVÀdÐQV�v˜d˜V 2 c U¨+°e°W¸DÕAÒZ^ùs   ‚)	ÚSITE_URLÚLINK_CARD_PATHr   Ú
LINK_TITLEÚLINK_DESCRIPTIONr   ÚLINK_CARD_WIDTHÚLINK_CARD_HEIGHTrÌ   )ÚimagerÒ   rë   Útagss       r    rà   rà   Å  sç   € ä”~Ñ%€EÜ”: TÑ*€EÜÔ)°Ñ6€Kà	� Ð,Ø*Ø.Ø	�Xœx¨#™~Ð.Ø	�Z Ð'Ø	Ð% {Ð3Ø	�Z Ð'Ø	Ð*¨EÐ2Ø2Ø	Ð%¤s¬?Ó';Ð<Ø	Ð&¬Ô,<Ó(=Ð>Ø	�^ UÐ+Ø7Ø	� %Ð(Ø	Ð&¨Ð4Ø	� %Ð(Ø	Ð$ eÐ,ð#€Dð& �9‰9Ñ_ÑZ^Ó_Ó_Ð_r   )r>   r   r?   r   Úreturnr   )rG   r   rÿ   Úbytes)r>   r   rS   údict[str, Any]rÿ   r   r7   )
r>   r   r/   r-   rV   r   ra   úOptional[float]rÿ   úOptional[dict[str, Any]])r>   r   r2   r"   ra   r  rÿ   r   )rS   r  rÿ   r1   )
r>   r   r   r   r/   r-   ra   r  rÿ   r1   )r>   r   r/   r-   rÿ   r1   )r>   r   ru   r-   ra   r  rÿ   r   )r>   r   rq   r-   ra   r  rÿ   r-   )r~   r-   rÿ   r   )r€   r   r�   r   rÿ   r   )
rŠ   r   r‹   r   ru   r-   ra   r  rÿ   r   )r'   r   r‹   r   rÿ   r   )rŠ   r   r„   r   rÿ   r  )r¯   r   rÿ   r  )rŠ   r   r‹   r   r„   r-   rq   r-   rº   r   ra   r  rÿ   r)   )r/   r   rÂ   Úboolrÿ   r   )rÂ   r  rÿ   r   )r/   r   rÍ   r+   rÂ   r  rÿ   r   )rÒ   r   rÔ   r   rÕ   r   rÖ   r   rØ   r   r×   r   rº   r   rÎ   r   rÿ   r   )
rŠ   r   r‹   r   ru   r-   rº   r   rÿ   r   )r%   r   ræ   r   rº   r   rÿ   r   )rº   r   rÿ   r   )rÿ   r   )@r   Ú
__future__r   rD   r;   r8   rO   r`   Úurllib.errorrž   Úurllib.requestÚdataclassesr   r   Úhtmlr   Útypingr   r   Úurllib.parser	   rÊ   r÷   rø   rû   rü   rù   rú   ÚSESSION_HOURSre   rs   r‰   r¢   r   r"   r)   Ú	Exceptionr4   r@   rH   r:   rQ   rf   rj   rl   ro   rv   ry   rt   r‚   r�   r‘   r¬   r²   r¿   rÄ   rÆ   rÃ   rÙ   r0   râ   r¹   r¸   rà   r   r   r    Ú<module>r     sy  ðñõ: #ã Û Û Û Û Û Û ß )Ý ß  Ý "à	€ð *€Ø(€Ø€ØÐ Ø/€
ð<ð ð €Ø $Ñ&€à€Ø%€
ØÐ ñ �$Ñ÷ð ó ðñ �$Ñ÷ð ó ðñ �$Ñ÷(ð (ó ð(ô.�)ô .ôPôCô/ð JNðØðØ%ðØ-0ðØ7Fðàõö.Wô
ð  UYðØðØ"%ðØ.;ðØBQðàõôHö_ö
2ôô ö
=ôCôô:4ð(  ð&nØð&nà
ð&nð ð&nð ð	&nð
 ð&nð 
ð&nð õ&nôR?ô.ôð1	€ðz ñàðð ðð ð	ð
 ðð ðð ðð ðð ðð 	õô6ô"
ô	õ`r   