Ë
    pçj£Q  ã                   ól  — 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m	Z	m
Z
 ddlmZmZ ej                  j                  d«      xs4 ej                   j#                  ej                   j%                  e«      «      Zej                   j+                  ed«      Zej                   j+                  ed«      Zd\  ZZZd	\  ZZZZd
Zedz   Z dZ!dddddœZ"dddddœZ# ejH                  d«      Z%d„ Z&d„ Z'd„ Z(d„ Z)d„ Z*d„ Z+d„ Z,d'd„Z-d„ Z.d„ Z/d„ Z0d „ Z1 G d!„ d"e	«      Z2d'd#„Z3d$„ Z4d%„ Z5e6d&k(  r e5«        yy)(a<  The remote INBOX: the user chats with the two Stickbash coordinators from inbox.html (local or
https://bashatest.howincloud.com/inbox.html), this tiny server stores everything on the VM, and each coordinator picks
its messages up over ssh (_tools/inbox.sh) and posts replies, questions and status lines back. Python stdlib only.

Two coordinators ("lanes"): kills (the kill coordinator: kills, builds, engine, the game app) and live (the live-game
coordinator: online, shop, seasons, saves, accounts). The user addresses a message to kills, live or both.

  python3 inbox_server.py serve [port]            the HTTP API on 127.0.0.1 (Caddy proxies /api/inbox* to it)
  python3 inbox_server.py pull [--to LANE]        NEW user messages for LANE (default kills) as JSON, marked seen by LANE
  python3 inbox_server.py reply ID [--to LANE]    reply text on stdin; threads under message ID, marks it replied
  python3 inbox_server.py status [--to LANE]      LANE's "now" status line on stdin (shown on top of the page)
  python3 inbox_server.py ask                     JSON on stdin {to, topic, text, choices[], multi?}: a question for the user
                                                  (multi: true = the user may tick several choices; needs 2+ choices)
  python3 inbox_server.py post                    JSON on stdin {to, topic, text}: a note to the user (no answer needed)
  python3 inbox_server.py answers [--to LANE]     answers to LANE's questions not fetched before, marked fetched
  python3 inbox_server.py unask ID                withdraw an open question
  python3 inbox_server.py topics                  the topic list as JSON
  python3 inbox_server.py list                    everything, as JSON
  INBOX_DIR=/some/dir overrides where data.json + token live (tests).

HTTP (every call needs the header X-Inbox-Token = the token in ./token):
  GET  /api/inbox[?rev=N]   -> {rev, status, statusTs, statuses:{kills:{text,ts},live:{text,ts}}, topics:[...], messages:[...]}
                               (with ?rev=N equal to the current rev: just {rev, unchanged:true})
  POST /api/inbox {text, topic?, to?, parent?}  -> adds a user message (old pages send only {text}: topic general, to kills)
  POST /api/inbox/answer {id, answer}           -> the user answers question id (a single question: answer = text, one
                                                   of the choices or typed; a multi question: answer = a list of choices
                                                   plus at most one typed item; one string is taken as a one-item list)
  POST /api/inbox/topic {title}                 -> creates (or returns the existing) topic
Message fields: id, ts, kind (message|question|note), from (user|kills|live), to (kills|live|both|user), topic, parent,
text, state (message: new|seen|replied; question: open|answered|withdrawn; note: posted), seenTs, seenBy{lane:ts},
replies[{by,text,ts}], reply + replyTs (the latest coordinator reply, for the old pages), choices[], answer, answeredTs;
a multi-choice question also has multi: true and its answer is a list (single questions keep answer as text, no multi).
é    N)ÚBaseHTTPRequestHandlerÚThreadingHTTPServer)ÚurlparseÚparse_qsÚ	INBOX_DIRz	data.jsonÚtoken)ip  i¸  i   )éx   é   éÈ   i,  )ÚkillsÚlive)Úbothé   ÚgeneralÚGeneralÚ Úsystem©ÚidÚtitleÚtsÚbyz
page-sendszSent from pagesz^[A-Z][A-Z0-9 _/-]{2,40} v\d+\bc                  óR   — t        j                  dt        j                  «       «      S )Nz%Y-%m-%dT%H:%M:%SZ)ÚtimeÚstrftimeÚgmtime© ó    ú+/srv/stickbash-pages/_tools/inbox_server.pyÚnowr    4   s   € Ü�=‰=Ð-¬t¯{©{«}Ó=Ð=r   c                 ó|   — t        j                  dd| j                  «       «      j                  d«      d d }|xs dS )Nz
[^a-z0-9]+ú-é0   Útopic)ÚreÚsubÚlowerÚstrip)r   Úss     r   Úslugr*   8   s6   € Ü
�‰ˆ}˜c 5§;¡;£=Ó1×7Ñ7¸Ó<¸S¸bÐA€AØŠ<�Ðr   c                 óž  — | j                  dd«       | j                  dd«       | j                  dd«       | j                  dg «       | j                  dd«       | j                  d	i «      }t        D ]  }|j                  |ddd
œ«       Œ | d   r|d   d   s| d   | d   d
œ|d<   | j                  dg «      }|D �ch c]  }|d   ’Œ	 }}t        t        fD ]/  }|d   |vsŒ|j	                  |t        u rdndt        |«      «       Œ1 | d   D �],  }|j                  dd«       |j                  dd«       |j                  dd«       d|vr;|d   dk(  r.t        j                  |j                  dd«      «      r	t        d   nd|d<   |j                  dd«       |j                  dd«       |j                  dd«       d|vr|d   rd|d   |d   dœgng |d<   d|vr,|d   xs |j                  d«      dv r|d   nd}|rd|ini |d<   |j                  d |d   rt        |d   j                  «       «      nd«       �Œ/ t        | d!<   | S c c}w )"znBring any older store (v1: {status, statusTs, next, messages[{id,ts,text,state,reply,replyTs}]}) up to SCHEMA.Ústatusr   ÚstatusTsÚnexté   ÚmessagesÚrevr   Ústatuses©Útextr   r   r4   Útopicsr   ÚkindÚmessageÚfromÚuserÚtor$   r   ÚparentNÚreplyÚreplyTsÚreplies©r   r4   r   ÚseenByÚstate)ÚseenÚrepliedr   ÚseenTsÚv)Ú
setdefaultÚLANESÚGENERALÚPAGESÚinsertÚdictÚ	PAGE_SENDÚmatchÚgetÚminÚvaluesÚSCHEMA)ÚdÚstÚlaner5   ÚtÚidsÚmrB   s           r   ÚmigraterX   =   sU  € à‡L�L�˜2ÔØ‡L�L�˜RÔ Ø‡L�L�˜ÔØ‡L�L�˜RÔ Ø‡L�L�˜ÔØ	
�‰�j "Ó	%€BÜò 4ˆØ
�‰�d R¨rÑ2Õ3ð4àˆ‚{˜2˜g™; vÒ.Ø  ™{°!°J±-Ñ@ˆˆ7‰Ø�\‰\˜( BÓ'€FØ"Ö
#�qˆ1ˆT‹7Ð
#€CÐ
#Ü”uÐò =ˆØˆT‰7˜#ÒØ�M‰M˜q¤G™|™!°´D¸³GÕ<ð=ð ˆz‰]ó QˆØ	�‰�V˜YÔ'Ø	�‰�V˜VÔ$Ø	�‰�T˜7Ô#Ø˜!ÑØ()¨&©	°YÒ(>Ä9Ç?Á?ÐST×SXÑSXÐY_ÐacÓSdÔCeœ˜tšÐktˆAˆg‰JØ	�‰�X˜tÔ$Ø	�‰�W˜bÔ!Ø	�‰�Y Ô#Ø˜AÑØXYÐZaÒXb 7°A°g±JÀaÈ	ÁlÑSÑTÐhjˆAˆi‰LØ˜1ÑØ�Y‘<Ò]¨q¯u©u°W«~ÐATÑ/T A d¢GÐZ\ˆDÙ-1˜7 D™/°rˆAˆh‰KØ	�‰�X¸A¸hºKœs 1 X¡;×#5Ñ#5Ó#7Ô8ÈRÖPðQô €A€c�FØ€Hùò) $s   Â>I
c                  óâ   — 	 t        t        «      5 } t        j                  | «      }d d d «       t        «      S # 1 sw Y   ŒxY w# t        t        j
                  f$ r i }Y t        |«      S w xY w©N)ÚopenÚDATAÚjsonÚloadÚFileNotFoundErrorÚJSONDecodeErrorrX   )ÚfrR   s     r   r^   r^   a   sf   € ðÜ”$‹Zð 	˜1Ü—	‘	˜!“ˆA÷	ô �1‹:Ð÷		ð 	ûäœt×3Ñ3Ð4ò Ø‰Ü�1‹:Ððús$   ‚A ‘:§A ºA¿A ÁA.Á-A.c                 ó®  — t        t        dz   d«      5 }t        j                  |t        j                  «       t        «       } | |«      }|j                  dd«      dz   |d<   t        dz   }t        |d«      5 }t        j                  ||dd¬«       d	d	d	«       t        j                  |t        «       |cd	d	d	«       S # 1 sw Y   Œ.xY w# 1 sw Y   y	xY w)
zbRead-modify-write data.json under an exclusive lock, written atomically (no torn file on a crash).z.lockÚwr1   r   r/   z.tmpF©Úensure_asciiÚindentN)r[   r\   ÚfcntlÚflockÚLOCK_EXr^   rN   r]   ÚdumpÚosÚreplace)ÚfnÚlkrR   ÚoutÚtmpra   s         r   Úupdaterq   j   s²   € ä	Œd�W‰n˜cÓ	"ð 	 bÜ�‰�BœŸ™Ô&Ü‹FˆÙ�‹eˆØ—5‘5˜ “? QÑ&ˆˆ%‰Ü�V‰mˆÜ�#�s‹^ð 	:˜qÜ�I‰I�a˜¨°qÕ9÷	:ä
�
‰
�3œÔØ÷	ñ 	÷	:ð 	:ú÷	ð 	ús$   ”A$CÁ8B?Â#CÂ?C	ÃCÃCc                  óˆ   — t        t        «      5 } | j                  «       j                  «       cd d d «       S # 1 sw Y   y xY wrZ   )r[   Ú
TOKEN_FILEÚreadr(   )ra   s    r   r   r   x   s1   € Ü	ŒjÓ	ð  ˜QØ�v‰v‹x�~‰~Ó÷ ÷  ò  ús	   �8¸Ac                 ób   — t        | �| nd«      j                  dd«      j                  «       d | S )Nr   z
ú
)Ústrrl   r(   )r)   Úlimits     r   Úcleanry   }   s0   € Ü�A�M‰q rÓ*×2Ñ2°6¸4Ó@×FÑFÓHÈÈ%ÐPÐPr   c                 óš  — t        |t        «      }|sy|j                  «       }| d   D ]'  }|d   |k(  s|d   j                  «       |k(  sŒ"|d   c S  |syt        | d   «      t        k\  ryt        |«      | d   D �ch c]  }|d   ’Œ	 }}}|d}}||v rd||fz  |dz   }}||v rŒ| d   j                  ||t        «       |d	œ«       |S c c}w )
zaA topic by id or title (case-insensitive); created when create_by is given and it does not exist.r   r5   r   r   Nr   z%s-%dr/   r   )ry   Ú	MAX_TITLEr'   ÚlenÚ
MAX_TOPICSr*   Úappendr    )	rR   ÚrefÚ	create_byÚlowrU   ÚbaserV   ÚtidÚns	            r   Ú
find_topicr…   �   sï   € ä
�”YÓ
€CÙØØ
�)‰)‹+€CØˆx‰[ò ˆØˆT‰7�cŠ>˜Q˜w™Z×-Ñ-Ó/°3Ó6Ø�T‘7ŠNðñ ØÜ
ˆ1ˆX‰;Óœ:Ò%ØÜ�S“	¨Q¨x©[Ö9¨˜A˜d›GÐ9ˆ#Ð9€DØ�1ˆ€CØ
�‰*Ø˜D !˜9Ñ$ a¨!¡eˆQˆð �Š*à€h�K×Ñ˜c¨C´s³uÀIÑNÔOØ€Jùò :s   Á=Cc                 óÂ   — | d   t        «       ddddd ddddg i ddœ}|j                  |«       | dxx   d	z  cc<   | d
   j                  |«       | d
   t         d  | d
<   |S )Nr.   r7   r9   r   r   r   Únew)r   r   r6   r8   r:   r$   r;   r4   rA   r<   r=   r>   r@   rD   r/   r0   )r    rq   r~   ÚMAX_MESSAGES)rR   ÚkwrW   s      r   Únew_msgrŠ   –   sx   € Ø�‰y¤£¨yÀ&ÐPWÐbkÐw{Ø˜e¨b¸RÈBÐZ\Ðhjñ	l€Aà‡H�HˆR„LØ€fƒI��NƒIØ€j�M×Ñ˜ÔØ�j‘M¤< - .Ð1€A€j�MØ€Hr   c                 óR  ‡— t        |j                  d«      t        «      }|st        d«      ‚|j                  d«      xs d}|t        vrt        d«      ‚|j                  d«      Šd }‰dvr1	 t        ‰«      Št        ˆfd	„| d
   D «       d «      }|st        d«      ‚d Š|r|d   }nSd|v r/|j                  d«      dvrt        | |d   «      }|€+t        d«      ‚t        j                  |«      r	t        d   nd}t        | |||‰¬«      S # t        t        f$ r t        d«      ‚w xY w)Nr4   zempty messager:   r   zto must be kills, live or bothr;   )Nr   r   z
bad parentc              3   ó4   •K  — | ]  }|d    ‰k(  sŒ|–— Œ y­w©r   Nr   )Ú.0rW   r;   s     €r   ú	<genexpr>z#add_user_message.<locals>.<genexpr>®   s   øè ø€ ÒA˜¨q°©w¸&Ó/@”1ÑAùó   ƒ‘r0   zno such parent messager$   )Nr   zno such topicr   r   )r4   r:   r$   r;   )ry   rN   ÚMAX_TEXTÚ
ValueErrorÚTO_USERÚintÚ	TypeErrorr.   r…   rL   rM   rI   rŠ   )rR   Úbodyr4   r:   Úpmr$   r;   s         @r   Úadd_user_messager˜       s.  ø€ Ü�—‘˜&Ó!¤8Ó,€DÙÜ˜Ó)Ð)Ø	�‰�$‹Ò	"˜7€BØ	”ÑÜÐ9Ó:Ð:Ø�X‰X�hÓ€FØ	€BØ�]Ñ"ð	+Ü˜“[ˆFô ÓA˜a 
™mÔAÀ4ÓHˆÙÜÐ5Ó6Ð6àˆÙ	Ø�7‘‰Ø	�D‰˜TŸX™X gÓ.°jÑ@Ü˜1˜d 7™mÓ,ˆØˆ=Ü˜_Ó-Ð-ä(Ÿ™¨tÔ4”�d’¸)ˆÜ�1˜4 B¨e¸FÔCÐCøô œ:Ð&ò 	+Ü˜\Ó*Ð*ð	+ús   Á.D ÄD&c                 ó  ‡— 	 t        |j                  d«      «      Št	        ˆfd„| d   D «       d «      }|st        d«      ‚|d   dk(  rt        d«      ‚|j                  d	«      }|j                  d
«      rt        ||«      }nKt        |t        «      rt        |«      dk7  rt        d«      ‚|d   }t        |t        «      }|st        d«      ‚|t        «       ddf\  |d	<   |d<   |d<   |d<   |S # t        t        f$ r t        d«      ‚w xY w)Nr   zbad idc              3   óF   •K  — | ]  }|d    ‰k(  sŒ|d   dk(  sŒ|–— Œ y­w)r   r6   ÚquestionNr   )rŽ   rW   Úqids     €r   r�   z"answer_question.<locals>.<genexpr>Ã   s&   øè ø€ ÒU�A¨!¨D©'°S«.¸Q¸v¹YÈ*Ó=TŒaÑUùs   ƒ!‘!š!r0   zno such questionrA   Ú	withdrawnzthat question was withdrawnÚanswerÚmultir/   zthat question takes one answerr   zempty answerÚansweredFÚ
answeredTsÚanswerPulled)r”   rN   r•   r’   r.   Úmulti_answerÚ
isinstanceÚlistr|   ry   r‘   r    )rR   r–   ÚqÚrawÚansrœ   s        @r   Úanswer_questionr©   ¾   s  ø€ ð#Ü�$—(‘(˜4“.Ó!ˆô 	ÓU˜˜:™ÔUÐW[Ó\€AÙÜÐ+Ó,Ð,Øˆ�z�[Ò ÜÐ6Ó7Ð7Ø
�(‰(�8Ó
€CØ‡u�uˆW„~Ü˜1˜cÓ"‰ä�cœ4Ô Ü�3‹x˜1Š}Ü Ð!AÓBÐBØ�a‘&ˆCÜ�CœÓ"ˆÙÜ˜^Ó,Ð,ØBEÄsÃuÈjÐZ_ÐB_Ñ?€A€h�K��<‘ ! G¡*¨a°Ñ.?Ø€Høô' ”zÐ"ò #Ü˜Ó"Ð"ð#ús   ƒC0 Ã0Dc                 ó>  — t        |t        «      r|g}t        |t        «      st        d«      ‚t	        |«      t
        dz   kD  rt        d«      ‚| j                  d«      xs g }t        «       g }}|D ]\  }t        |t        «      st        d«      ‚t        |t        «      }|sŒ1||v r|j                  |«       ŒG||vsŒL|j                  |«       Œ^ t	        |«      dkD  rt        d|d   dd z  «      ‚|D �cg c]	  }||v sŒ|‘Œ c}|z   }|st        d	«      ‚|S c c}w )
zÚA multi question's answer: a list of its choices (kept in the choices' order) plus at most one typed "other"
    answer (last), the same free text a single question takes. An old page sends one string: a one-item list.zanswer must be a listr/   ztoo many answersÚchoiceszeach answer must be textz:not one of the choices (only one typed answer allowed): %sNéP   zpick at least one)r¤   rw   r¥   r’   r|   ÚMAX_CHOICESrN   Úsetry   r‘   Úaddr~   )	r¦   r§   r«   ÚpickedÚothersÚitemr)   Úcr¨   s	            r   r£   r£   ×   s#  € ô �#”sÔØˆeˆÜ�cœ4Ô ÜÐ0Ó1Ð1Ü
ˆ3ƒx”+ ‘/Ò!ÜÐ+Ó,Ð,Ø�e‰e�IÓÒ$ "€GÜ“U˜BˆF€FØò 	ˆÜ˜$¤Ô$ÜÐ7Ó8Ð8Ü�$œÓ!ˆÙØØ�‰<Ø�J‰J�q�MØ�fŠ_Ø�M‰M˜!Õð	ô ˆ6ƒ{�Q‚ÜÐUÐX^Ð_`ÑXaÐbeÐceÐXfÑfÓgÐgØÖ
-�  f¢Š1Ò
-°Ñ
6€CÙÜÐ,Ó-Ð-Ø€Jùò .s   Ã7	DÄDc                   ó@   — e Zd ZdZd„ Zd„ Zd„ Zd„ Zd„ Zd„ Z	d„ Z
d	„ Zy
)ÚApizStickbashInbox/2c                 óv  — t        j                  |d¬«      j                  «       }| j                  |«       | j	                  dd«       | j	                  dt        t        |«      «      «       | j	                  dd«       | j                  «        | j                  «        | j                  j                  |«       y )NF)re   zContent-Typezapplication/json; charset=utf-8úContent-LengthzCache-Controlzno-store)r]   ÚdumpsÚencodeÚsend_responseÚsend_headerrw   r|   Ú_corsÚend_headersÚwfileÚwrite)ÚselfÚcodeÚobjr–   s       r   Ú_sendz	Api._send÷   sŠ   € Ü�z‰z˜#¨EÔ2×9Ñ9Ó;ˆØ×Ñ˜4Ô Ø×Ñ˜Ð)JÔKØ×ÑÐ)¬3¬s°4«y«>Ô:Ø×Ñ˜¨*Ô5Ø�
‰
ŒØ×ÑÔØ�
‰
×Ñ˜Õr   c                 óp   — | j                  dd«       | j                  dd«       | j                  dd«       y )NzAccess-Control-Allow-OriginÚ*zAccess-Control-Allow-MethodszGET, POST, OPTIONSzAccess-Control-Allow-HeaderszContent-Type, X-Inbox-Token)r»   ©rÀ   s    r   r¼   z	Api._cors  s7   € à×ÑÐ6¸Ô<Ø×ÑÐ7Ð9MÔNØ×ÑÐ7Ð9VÕWr   c                 óÈ   — | j                   j                  dd«      }t        |«      xr: t        j                  |j                  «       t        «       j                  «       «      S )NzX-Inbox-Tokenr   )ÚheadersrN   ÚboolÚhmacÚcompare_digestr¹   r   )rÀ   Úgivens     r   Ú_authedzApi._authed  sC   € Ø—‘× Ñ  °"Ó5ˆÜ�E‹{ÒTœt×2Ñ2°5·<±<³>Ä5Ã7Ç>Á>ÓCSÓTÐTr   c                 ó
  — t        | j                  «      }|j                  j                  d«      }|dk(  rdt        |j                  «      fS |j                  d«      r#|t        d«      d  t        |j                  «      fS d i fS )Nú/z
/api/inboxr   z/api/inbox/)r   ÚpathÚrstripr   ÚqueryÚ
startswithr|   )rÀ   ÚprÐ   s      r   Ú_routez
Api._route  st   € Ü�T—Y‘YÓˆØ�v‰v�}‰}˜SÓ!ˆØ�<ÒØ”x §¡Ó(Ð(Ð(Ø�?‰?˜=Ô)Øœ˜MÓ*Ð+Ð,¬h°q·w±wÓ.?Ð?Ð?Ø�Rˆxˆr   c                 óf   — | j                  d«       | j                  «        | j                  «        y )NéÌ   )rº   r¼   r½   rÆ   s    r   Ú
do_OPTIONSzApi.do_OPTIONS  s%   € Ø×Ñ˜3ÔØ�
‰
ŒØ×ÑÕr   c           
      óˆ  — | j                  «       \  }}|dk7  r| j                  dddi«      S | j                  «       s| j                  dddi«      S t        «       }|j	                  ddg«      d   t        |d   «      k(  r| j                  d	|d   d
dœ«      S | j                  d	|d   |d   |d   |d   |d   |d   dœ«       y )Nr   é”  Úerrorú	not foundé‘  úwrong or missing tokenr1   r   r   T)r1   Ú	unchangedr,   r-   r2   r5   r0   )r1   r,   r-   r2   r5   r0   )rÕ   rÃ   rÍ   r^   rN   rw   )rÀ   r&   ÚqsrR   s       r   Údo_GETz
Api.do_GET  sÎ   € Ø—+‘+“-‰ˆˆRØ�"Š9Ø—:‘:˜c G¨[Ð#9Ó:Ð:Ø�|‰|Œ~Ø—:‘:˜c GÐ-EÐ#FÓGÐGÜ‹FˆØ�6‰6�%˜"˜Ó˜qÑ!¤S¨¨5©£]Ò2Ø—:‘:˜c¨1¨U©8À$Ñ#GÓHÐHØ�
‰
�3  %¡°A°h±KÈQÈzÉ]ÐhiÐjtÑhuØ#$ X¡;¸A¸j¹MñKõ 	Lr   c                 óŠ  ‡‡— | j                  «       \  }}|dvr| j                  dddi«      S | j                  «       s| j                  dddi«      S 	 t        | j                  j                  dd«      «      }|t        kD  r| j                  d	dd
i«      S t        j                  | j                  j                  |«      xs d«      Št        ‰t        «      st        ‚	 	 |dk(  rt        ˆfd„«      }nY|dk(  rt        ˆfd„«      }nEt!        ‰j                  d«      t"        «      Š‰s| j                  dddi«      S ˆfd„}t        |«      }| j                  d|«       y # t        t        j                  f$ r | j                  dddi«      cY S w xY w# t        $ r'}| j                  ddt%        |«      i«      cY d }~S d }~ww xY w)N)r   rž   r$   rÚ   rÛ   rÜ   rÝ   rÞ   r·   Ú0i�  ztoo bigs   {}é�  zbad jsonr   c                 ó   •— t        | ‰«      S rZ   )r˜   ©rR   r–   s    €r   ú<lambda>zApi.do_POST.<locals>.<lambda>6  s   ø€ Ô'7¸¸4Ó'@€ r   rž   c                 ó   •— t        | ‰«      S rZ   )r©   ræ   s    €r   rç   zApi.do_POST.<locals>.<lambda>8  s   ø€ ¤°q¸$Ó'?€ r   r   zempty titlec                 óP   •‡— t        | ‰d¬«      Št        ˆfd„| d   D «       «      S )Nr9   ©r€   c              3   ó4   •K  — | ]  }|d    ‰k(  sŒ|–— Œ y­wr�   r   )rŽ   rU   rƒ   s     €r   r�   z*Api.do_POST.<locals>.mk.<locals>.<genexpr>@  s   øè ø€ ÒI a¸!¸D¹'ÀS».¤ÑIùr�   r5   )r…   r.   )rR   rƒ   r   s    @€r   ÚmkzApi.do_POST.<locals>.mk>  s&   ù€ Ü$ Q¨¸Ô@�CÜÓI¨1¨X©;ÔIÓIÐIr   r   )rÕ   rÃ   rÍ   r”   rÈ   rN   ÚMAX_BODYr]   ÚloadsÚrfilert   r¤   rK   r’   r`   rq   ry   r{   rw   )	rÀ   r&   Ú_r„   ro   rì   Úer–   r   s	          @@r   Údo_POSTzApi.do_POST%  s™  ù€ Ø—‘“‰ˆˆQØÐ-Ñ-Ø—:‘:˜c G¨[Ð#9Ó:Ð:Ø�|‰|Œ~Ø—:‘:˜c GÐ-EÐ#FÓGÐGð	:Ü�D—L‘L×$Ñ$Ð%5°sÓ;Ó<ˆAØ”8Š|Ø—z‘z #¨°Ð';Ó<Ð<Ü—:‘:˜dŸj™jŸo™o¨aÓ0Ò9°EÓ:ˆDÜ˜d¤DÔ)Ü Ð ð *ð	6Ø�bŠyÜÓ@ÓA‘Ø˜’ÜÓ?Ó@‘ä˜dŸh™h wÓ/´Ó;�ÙØŸ:™: c¨G°]Ð+CÓDÐDôJô ˜R“j�ð 	�
‰
�3˜Õøô% œD×0Ñ0Ð1ò 	:Ø—:‘:˜c G¨ZÐ#8Ó9Ò9ð	:ûô  ò 	6Ø—:‘:˜c G¬S°«VÐ#4Ó5Õ5ûð	6ús?   ÁAE ÂAE ÃAF Ä<F Å-FÆFÆ	GÆF=Æ7GÆ=Gc                 óš   — t         j                  j                  | j                  ›d| j                  j                  d«      d   ›d�«       y )Nú ú?r   rv   )ÚsysÚstderrr¿   ÚcommandrÐ   Úsplit)rÀ   ÚfmtÚargss      r   Úlog_messagezApi.log_messageF  s/   € Ü�
‰
×Ñ d§l£l°D·I±I·O±OÀCÓ4HÈÓ4KÐLÕMr   N)Ú__name__Ú
__module__Ú__qualname__Úserver_versionrÃ   r¼   rÍ   rÕ   rØ   rá   rò   rü   r   r   r   rµ   rµ   ô   s4   „ Ø'€NòòXòUòòò

LòóBNr   rµ   c                 óÐ   — |dk(  r|dfn|f}|D ]T  }|| v sŒ| j                  |«      }|dz   t        | «      k\  rt        j                  d|z  «       | |dz      }| ||dz   …= |c S  |S )zB--name VALUE out of args (removed); also --as as an alias of --to.ú--toz--asr/   z%s needs a valuer   )Úindexr|   rö   Úexit)rû   ÚnameÚdefaultÚnamesÚnmÚiÚvals          r   Úoptr  J  s‚   € à" fšnˆT�6‰N°4°'€EØò ˆØ�Š:Ø—
‘
˜2“ˆAØ�1‰uœ˜D›	Ò!Ü—‘Ð+¨bÑ0Ô1Ø�q˜1‘u‘+ˆCØ�Q�q˜1‘u�W�ØŠJðð €Nr   c                 óœ   — t        | dt        j                  j                  d«      xs d«      }|t        vrt        j                  d«       |S )Nr  ÚINBOX_ASr   z--to must be kills or live)r  rk   ÚenvironrN   rG   rö   r  )rû   rT   s     r   Úlane_ofr  X  s<   € Üˆt�VœRŸZ™ZŸ^™^¨JÓ7ÒB¸7ÓC€DØ”5ÑÜ�‰Ð-Ô.Ø€Kr   c                  ón	  ‡‡‡‡‡‡‡‡— t         j                  dd  } | r| j                  d«      ndŠ‰dk(  r3| rt        | d   «      nd}t	        d|ft
        «      j                  «        y ‰dk(  rgt        | «      Šˆfd„}t        |«      \  }}t        t        j                  |d	d¬
«      «       |r%t         j                  j                  d|‰fz  «       y y ‰dk(  r£| st        j                  d«       t        | «      Št        | d   «      t        t         j                   j#                  «       t$        «      cŠŠ‰st        j                  d«       ˆˆˆfd„}t        j                  t        |«      rd«       y d«       y ‰dk(  rEt        | «      Št        t         j                   j#                  «       d«      Šˆˆfd„}t        |«       y ‰dv �rÀ	 t        j&                  t         j                   j#                  «       xs d«      Š‰j+                  d«      xs# t,        j.                  j+                  d«      xs dŠ‰t0        vrt        j                  d«       t        ‰j+                  d«      t$        «      Š‰st        j                  d«       ‰j+                  d«      xs g D �cg c]#  }t        |t2        «      sŒt        |t2        «      ‘Œ% c}d t4         Š‰dk(  xr ‰j+                  d«      du Š‰rt7        t8        j;                  ‰«      «      Š‰r#t=        ‰«      d k  rt        j                  d!«       ˆˆˆˆˆˆfd"„}t        |«      }	|	d#   |	d$   |	d%   d&œ}|	j+                  d«      rd|d<   t        t        j                  |«      «       y ‰d'k(  r;t        | «      Šˆfd(„}
t        t        j                  t        |
«      d	d¬
«      «       y ‰d)k(  r;t        | d   «      Šˆfd*„}t        j                  t        |«      rd«       y d«       y ‰d+k(  r-t        t        j                  t?        «       d+   d	d¬
«      «       y ‰dk(  r*t        t        j                  t?        «       d	d¬
«      «       y t        j                  d,«       y # t        j(                  $ r t        j                  d«       Y �Œ¬w xY wc c}w )-Nr/   r   r¥   ÚserveiV"  z	127.0.0.1Úpullc           	      ó   •‡— t        «       g }}| d   D ]Î  Š‰d   dk7  s‰d   dk7  s	‰d   ‰dfvrŒ‰d   j                  ‰«      rŒ2‰d   ‰k(  r	‰d	   d
k7  rŒC|‰d   ‰<   ‰d   xs |‰d<   ‰d	   d
k(  rd‰d	<   t        ‰t        ˆfd„| d   D «       ‰d   «      ¬«      }‰d   rt        ˆfd„| d   D «       d «      nd }|r|d   d d |d<   |d   |d<   |j	                  |«       ŒÐ t        ˆfd„| d   D «       «      }||fS )Nr0   r6   r7   r8   r9   r:   r   r@   rA   r‡   rD   rB   c              3   ó@   •K  — | ]  }|d    ‰d   k(  sŒ|d   –— Œ y­w)r   r$   r   Nr   )rŽ   rU   rW   s     €r   r�   z$cli.<locals>.take.<locals>.<genexpr>u  s*   øè ø€ Ò,f¸AÐPQÐRVÑPWÐ[\Ð]dÑ[eÓPe¨Q¨w­ZÑ,fùs   ƒ”
r5   r$   )Ú
topicTitler;   c              3   ó:   •K  — | ]  }|d    ‰d   k(  sŒ|–— Œ y­w)r   r;   Nr   )rŽ   ÚxrW   s     €r   r�   z$cli.<locals>.take.<locals>.<genexpr>v  s    øè ø€ ÒN °q¸±wÀ!ÀHÁ+Ó7Mœ1ÑNùs   ƒ”r4   rä   Ú
parentTextÚ
parentFromc              3   óx   •K  — | ]1  }|d    dk(  sŒ|d   ‰k(  sŒ|d   dk(  r|j                  d«      sd–— Œ3 y­w)r6   r›   r8   rA   r    r¢   r/   N)rN   )rŽ   rW   rT   s     €r   r�   z$cli.<locals>.take.<locals>.<genexpr>{  sK   øè ø€ ò V °a¸±iÀ:Ó6MÐRSÐTZÑR[Ð_cÓRcØ ™j¨JÒ6¸q¿u¹uÀ^Ô?Tô ñ Vùs   ƒ:‘:š :)r    rN   rK   r.   r~   Úsum)rR   rU   ro   Úor—   ÚpendingrW   rT   s         @€r   Útakezcli.<locals>.takeh  sL  ù€ Ü“U˜BˆsˆAØ�z‘]ò �Ø�V‘9 	Ò)¨Q¨v©Y¸&Ò-@ÀAÀdÁGÐTXÐZ`ÐSaÑDaØØ�X‘;—?‘? 4Ô(ØØ�T‘7˜d’? q¨¡z°UÒ':ØØ$%��(‘˜DÑ!Ø ™kÒ.¨Q��(‘Ø�W‘: Ò&Ø!'�A�g‘JÜ˜¤tÓ,fÀÀ8ÁÔ,fÐhiÐjqÑhrÓ'sÔt�ØYZÐ[cÒYd”TÓN a¨
¡mÔNÐPTÔUÐjn�ÙØ&(¨¡j°°#Ð&6�A�l‘OØ&(¨¡j�A�l‘OØ—
‘
˜1•ð!ô" ó V Q z¡]ô Vó VˆGà˜�<Ðr   Frd   zHinbox: %d new answer(s) to your questions: run inbox.sh answers --to %s
r<   z+usage: reply ID [--to LANE] (text on stdin)zempty replyc                 óä   •— | d   D ]g  }|d   ‰k(  sŒt        «       }|d   j                  ‰‰|dœ«       ‰|c|d<   |d<   |d   j                  ‰|«       |d   xs ||d<   |d	   d
k(  rd|d<    y y)Nr0   r   r>   r?   r<   r=   r@   rD   r6   r7   rC   rA   TF)r    r~   rF   )rR   rW   rU   rT   Úmidr4   s      €€€r   Úrepzcli.<locals>.repŠ  s˜   ø€ Ø�z‘]ò 	 �Ø�T‘7˜c“>Ü›�AØ�i‘L×'Ñ'¨t¸TÈÑ(KÔLØ/3°QÐ,�A�g‘J  )¡Ø�h‘K×*Ñ*¨4°Ô3Ø"# H¡+Ò"2°�A�h‘KØ˜‘y IÒ-Ø%.˜˜'™
Ùð	 ð r   r,   iè  c                 óR   •— t        «       }‰|dœ| d   ‰<   ‰dk(  r‰|c| d<   | d<   y y )Nr3   r2   r   r,   r-   )r    )rR   rU   rT   r4   s     €€r   rS   zcli.<locals>.st›  s=   ø€ Ü“ˆAØ+/°qÑ"9ˆAˆj‰M˜$ÑØ�wŠØ-1°1Ð*��(‘˜Q˜zš]ð r   )ÚaskÚpostz{}zbad json on stdinr:   r  r   zto must be kills or liver4   z
empty textr«   r#  rŸ   Tr   z"--multi needs at least 2 --choicesc                 óð   •— t        | ‰j                  d«      xs d‰¬«      }‰dk(  r7‰rdg dœni }t        | fddid	‰i¤d
|‰	d‰ddddœ¤Ž}|j                  |«       |S t        | fddid	‰i¤d
|‰	ddœ¤Ž}|S )Nr$   r   rê   r#  T)rŸ   rž   r6   r›   r8   r9   r[   r   F)r:   r$   r4   rA   r«   rž   r¡   r¢   ÚnoteÚposted)r:   r$   r4   rA   )r…   rN   rŠ   rq   )
rR   r$   ÚextrarW   r–   r«   ÚcmdrT   rŸ   r4   s
       €€€€€€r   r#  zcli.<locals>.ask³  s®   ø€ Ü˜q $§(¡(¨7Ó"3Ò"@°yÈDÔQˆEØ�eŠ|Ù9> $°"Ò5ÀB�Ü˜Añ [ Jð [°6¸4°.ð [ÀVÐSXÐ_cÐkqØ$+°BÀ2ÐTYó[�à—‘˜”ð ˆHô ˜AÑp FÐp¨v°t¨nÐpÀÈuÐ[_ÐgoÓp�ØˆHr   r   r$   r6   )r   r$   r6   Úanswersc                 ó"  •— g }| d   D ]ƒ  }|d   dk(  sŒ|d   ‰k(  sŒ|d   dk(  sŒ|j                  d«      rŒ0d|d<   |d	   |d
   |d   |j                  dg «      |d   |d   dœ}|j                  d«      rd|d<   |j                  |«       Œ… |S )Nr0   r6   r›   r8   rA   r    r¢   Tr   r$   r4   r«   rž   r¡   )r   r$   r›   r«   rž   r¡   rŸ   )rN   r~   )rR   ro   rW   ÚarT   s       €r   r¨   zcli.<locals>.ansÅ  s·   ø€ ØˆCØ�z‘]ò "�Ø�V‘9 
Ó*¨q°©y¸DÓ/@ÀQÀwÁZÐS]ÓE]Ðfg×fkÑfkÐlzÕf{Ø(,�A�nÑ%Ø  ™w°°7±ÈÈ6ÉÐ_`×_dÑ_dÐenÐprÓ_sØ#$ X¡;¸aÀ¹oñO�Aà—u‘u˜W”~Ø%)˜˜'™
Ø—J‘J˜q•Mð"ð ˆJr   Úunaskc                 ó\   •— | d   D ]#  }|d   ‰k(  sŒ|d   dk(  sŒ|d   dk(  sŒd|d<    y y	)
Nr0   r   r6   r›   rA   r[   r�   TFr   )rR   rW   rœ   s     €r   Úwdzcli.<locals>.wdÔ  sI   ø€ Ø�z‘]ò  �Ø�T‘7˜c“> a¨¡i°:Ó&=À!ÀGÁ*ÐPVÓBVØ!,�A�g‘JÙð ð r   r5   zpusage: inbox_server.py serve [port] | pull | reply ID | status | ask | post | answers | unask ID | topics | list) rö   ÚargvÚpopr”   r   rµ   Úserve_foreverr  rq   Úprintr]   r¸   r÷   r¿   r  ry   Ústdinrt   r‘   rî   r`   rN   rk   r  rG   Ú
MAX_CHOICEr­   r¥   rK   Úfromkeysr|   r^   )rû   Úportr  ro   r  r!  rS   r³   r#  rW   r¨   r/  r–   r«   r)  rT   r   rŸ   rœ   r4   s               @@@@@@@@r   Úclir8  _  s�  ÿ€ Ü�8‰8�A�Bˆ<€DÙˆ$�(‰(�1Œ+ 6€CØ
ˆg‚~Ù#Œs�4˜‘7Œ|¨ˆÜ˜[¨$Ð/´Ó5×CÑCÕEØ	�ŠÜ�t‹}ˆô	 ô, ˜d“|‰ˆˆWÜŒd�j‰j˜¨5¸Ô;Ô<ÙÜ�J‰J×ÑÐhÐlsÐuyÐkzÑzÕ{ð à	�ŠÙÜ�H‰HÐBÔCÜ�t‹}ˆÜ˜˜Q™“L¤%¬¯	©	¯©Ó(8¼(Ó"Cˆ	ˆˆTÙÜ�H‰H�]Ô#ö	ô 	�‰”f˜S”k�Õ) qÕ)Ø	�ŠÜ�t‹}ˆÜ”S—Y‘Y—^‘^Ó% tÓ,ˆõ	5ô
 	ˆr�
Ø	�Ò	ð	*Ü—:‘:œcŸi™iŸn™nÓ.Ò6°$Ó7ˆDð �x‰x˜‹~ÒF¤§¡§¡°
Ó!;ÒF¸wˆØ”uÑÜ�H‰HÐ/Ô0Ü�T—X‘X˜fÓ%¤xÓ0ˆÙÜ�H‰H�\Ô"Ø26·(±(¸9Ó2EÒ2KÈÖe¨AÔPUÐVWÔYcÕPd”5˜œJÕ'ÒeÐfrÔgrÐsˆØ�u‘Ò: §¡¨'Ó!2°dÐ!:ˆÙÜœ4Ÿ=™=¨Ó1Ó2ˆGÙ”S˜“\ AÒ%Ü�H‰HÐ9Ô:÷		ñ 		ô �3‹KˆØ�t‘W q¨¡z¸1¸V¹9ÑEˆØ�5‰5�Œ>ØˆC�‰LÜŒd�j‰j˜‹oÕØ	�	Ò	Ü�t‹}ˆô
	ô 	Œd�j‰jœ ›°5ÀÔCÕDØ	�ŠÜ�$�q‘'‹lˆô	ô 	�‰”f˜R”j�Õ( aÕ(Ø	�ŠÜŒd�j‰jœ› Ñ)¸ÀaÔHÕIØ	�ŠÜŒd�j‰jœ›¨e¸AÔ>Õ?ä�‰ð  Dõ  	Eøôy ×#Ñ#ò 	*Ü�H‰HÐ(×)ð	*üò fs   Ç5R Ê"R2Ê8R2Ò(R/Ò.R/Ú__main__rZ   )7Ú__doc__rg   rÊ   r]   rk   r%   rö   r   Úhttp.serverr   r   Úurllib.parser   r   r  rN   rÐ   ÚdirnameÚabspathÚ__file__ÚHEREÚjoinr\   rs   r‘   rˆ   rí   r{   r­   r5  r}   rG   r“   rQ   rH   rI   ÚcompilerL   r    r*   rX   r^   rq   r   ry   r…   rŠ   r˜   r©   r£   rµ   r  r  r8  rý   r   r   r   ú<module>rC     sK  ðñ ÷B ,× +× +Ó +ß Cß +à	‡z�z‡~�~�kÓ"ÒP b§g¡g§o¡o°b·g±g·o±oÀhÓ6OÓ&P€Ø	‡w�w‡|�|�D˜+Ó&€Ø�W‰W�\‰\˜$ Ó(€
Ø#8Ñ  €ˆ,˜Ø1AÑ .€	ˆ;˜
 JØ€Ø
�)Ñ
€Ø	
€Ø Y°bÀÑ
I€ØÐ&7¸rÈÑR€ØˆB�J‰JÐ9Ó:€	ò>òò
!òHòò ò
Qóò*òDò<ò2ô:SNÐ
 ô SNólòòAEðH ˆzÒÙ…Eð r   