
    i_*                       S SK Jr  S SKrS SKrS SKJr  S SKJr  S SKJrJ	r	J
r
JrJr  S SKJrJrJrJrJrJr  S SKJrJr  S SKJr  S S	KJr  S S
KJr  S SKJ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S/S9r+\\\" \5      4   r,\" SS9r-S q.S/ S.S/ SQS.S/ SQS.S/ SQS.S/ SQS.S/ S QS.S!/ S"QS.S#/ S$QS.S%/ S&QS.S'.	r/ " S( S)\5      r0 " S* S+\5      r1\+Re                  S,5      SIS- j5       r3\+Re                  S.5      SJS/ j5       r4\+Rk                  S05      S1 5       r6\+Rk                  S25      \" S3S4S5S69\" S7S8S99\" S7S:S99\" S7S;S994       SKS< jj5       r7 " S= S>\5      r8 " S? S@\5      r9 SL SMSA jjr:\+Re                  SB\" \:5      /SC9SNSD j5       r;\+Re                  SE5      SOSF j5       r<\+Re                  SG\" \:5      /SC9      SPSH j5       r=g)Q    )annotationsN)deque)datetime)	AnnotatedAnyDictListOptional)	APIRouterDependsHeaderHTTPExceptionQuerystatus)	BaseModelField)select)AsyncSession)settings)get_session)Poll
PollStatus)ws)hihaho_clientvoting)parse_branchesz/hihahohihaho)prefixtags   )maxlenz3The hihaho interactive layer / API has been loaded.)descriptionfieldsz!The viewer has started the video.)actorpercentageToSucceed
session_idvideo_id
video_uuidz,The viewer has reached the end of the video.)
r$   historyscoremaxScorescaledResultr%   passedr&   r'   r(   z#The viewer has answered a question.)r$   correcttype
answerText	startTimeendTimepointsr)   r&   r'   r(   z.The viewer has submitted an optional variable.)namevaluer&   r'   r(   z)The viewer has clicked on an interaction.)interaction_namer5   actionr&   r(   z&The viewer has clicked on a menu item.)titler7   r&   r'   r(   z)The viewer has clicked on a chapter item.)r8   
start_timer&   r(   zDPlayback heartbeat called roughly every 100ms while the video plays.)currentTimevolumeduration)	inithihaho_startedhihaho_endedhihaho_answer"hihaho_optional_variable_submittedhihaho_interaction_clickedhihaho_menu_item_clickedhihaho_chapter_item_clickedonTimec                  <    \ rS rSr% SrS\S'   S\S'   SrS\S'   S	rg)
BranchNotifyBodyY    strfilm_session_id
branch_keyNOptional[Dict[str, Any]]metadata )__name__
__module____qualname____firstlineno__rK   __annotations__rN   __static_attributes__rO       HC:\Users\jasmi\Projects\line-hihaho-voting\backend\app\routers\hihaho.pyrG   rG   Y   s    OSO)-H&-rV   rG   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
S\S	'   S
rS\S'   SrS\S'   SrS\S'   SrS\S'   Srg)DebugEventBody_   rI   rJ   room_idpoll_idfilm_urlevent_idorigin
event_typeFboolsource_is_iframereceived_at_clientNr   rawrM   rN   rO   )rP   rQ   rR   rS   r[   rT   r\   r]   r^   r_   r`   rb   rc   rd   rN   rU   rO   rV   rW   rY   rY   _   sh    GSGSHcHcFCJ"d"  CO)-H&-rV   rY   z/branchc                   #    [         R                  " U R                  =(       d    SU R                  U R                  S9I Sh  vN $  N7f)zJProxy to hihaho when API is configured; otherwise returns skipped payload.zdefault-session)rK   rL   rN   N)r   notify_branch_resultrK   rL   rN   )bodys    rW   notify_branchrh   l   sA      33,,A0A??   s   AA
AA
z/events/debugc                  #    [         S-  q U R                  5       n[         US'   [        R                  " 5       R	                  5       S-   US'   [
        R                  U5        SS[        [
        5      U R                  S.$ 7f)N   
server_seqZreceived_at_serverT)okstoredcountlatest_event_id)	
_DEBUG_SEQ
model_dumpr   utcnow	isoformat_DEBUG_EVENTSappendlenr^   )rg   payloads     rW   ingest_debug_eventrz   v   ss      !OJooG&GL$,OO$5$?$?$AC$GG !!]#==	 s   A?Bz/events/specc                    #    S[         S.$ 7f)NT)rn   items)HIHAHO_EVENT_SPECSrO   rV   rW   hihaho_event_specsr~      s      # s   
z/events/debug/latest   rj   d   )gelerI   zfilter by room id)r"   zfilter by poll idzsubstring matchc           
       #    [        [        5      nU(       a2  U Vs/ sH&  n[        UR                  SS5      5      U:X  d  M$  UPM(     nnU(       a2  U Vs/ sH&  n[        UR                  SS5      5      U:X  d  M$  UPM(     nnU(       aO  UR	                  5       nU Vs/ sH3  oV[        UR                  SS5      5      R	                  5       ;   d  M1  UPM5     nnX@* S  nS[        U5      US.$ s  snf s  snf s  snf 7f)Nr[   rI   r\   r`   T)rn   rp   r|   )listrv   rJ   getlowerrx   )limitr[   r\   r`   rowsrneedles          rW   latest_debug_eventsr      s      DH4a3quuY';#<#G4HH4a3quuY';#<#G4H!!#R4aS|R1H-I-O-O-Q#Q4R=DT  IH Ss:   C="C.C.	C="C3<C3C=/C8C8%C=c                  :    \ rS rSr% SrS\S'   \" SS9rS\S'   S	rg
)BranchOptionNames   rI   rJ   r8      )
min_length	List[str]option_namesrO   N)	rP   rQ   rR   rS   r8   rT   r   r   rU   rO   rV   rW   r   r      s    E3O#q1L)1rV   r   c                  P    \ rS rSr% S\S'   \" \S9rS\S'   \" S SS	9rS
\S'   Sr	g)InteractionSetupBody   zDict[str, Any]interactions)default_factoryzList[BranchOptionNames]branchesc                 
    SS/$ )Nu   選項Au   選項BrO   rO   rV   rW   <lambda>InteractionSetupBody.<lambda>   s	    I 6rV   zKFallback option names when branches list is shorter than detected branches.)r   r"   r   default_option_namesrO   N)
rP   rQ   rR   rS   rT   r   r   r   r   rU   rO   rV   rW   r   r      s0      (-d(CH%C&+6a') rV   r   c                   #    [         R                  (       d  g U [         R                  :w  a  [        [        R                  SS9eg 7f)Nzinvalid host key)status_codedetail)r   host_api_keyr   r   HTTP_403_FORBIDDEN)
x_host_keys    rW   _require_host_keyr      s:        X***(A(AJ\]] +s   AAz/rooms/{room_id}/reset)dependenciesc                  #    SSK Jn  SSKJn  UR	                  [        [        5      R                  [        R                  U :H  5      5      I Sh  vN R                  5       nU(       a  U Vs/ sH  oUR                  PM     nnUR	                  U" U5      R                  UR                  R                  U5      5      5      I Sh  vN   U H  nUR                  U5      I Sh  vN   M     UR                  5       I Sh  vN   [        R                  " U 5      I Sh  vN   S[!        U5      S.$  Ns  snf  Nq NU N; N7f)zADelete all polls/votes for a room and broadcast a clean snapshot.r   deleteVoteNT)rn   deleted_polls)
sqlalchemyr   app.models.entitiesr   execr   r   wherer[   allidr\   in_commit	ws_routerbroadcast_room_snapshotrx   )r[   session	sa_deleter   existingppoll_idss          rW   
reset_roomr      s      /(llt4<<723 
su  "*+(QDD(+ll9T?001A1A(1KLMMMA..### nn

+
+G
444X77 ,M#4sg   AE D1E .D3AE D8E $D:%E  D<E D>E 3E :E <E >E z/interactions/parsec                   #    U R                   R                  S/ 5      n[        U[        5      (       d  [	        SS5      e[        U5      nS[        U5      US.$ 7f)zHParse interaction JSON and return detected branch schedules (read-only).r     *interactions.interactions must be an arrayT)rn   branch_countr   )r   r   
isinstancer   r   r   rx   )rg   raw_interactionsr   s      rW   parse_interaction_jsonr      sX      ((,,^R@&--C!MNN./HH8LLs   AAz(/rooms/{room_id}/setup-from-interactionsc                  #    SSK Jn  SSKJn  SSKJnJn  UR                  R                  S/ 5      n[        U[        5      (       d  [        SS5      e[        U5      nU(       d  [        SS5      eU" X U 5      I S	h  vN   UR                  [        [        5      R!                  [        R"                  U :H  5      5      I S	h  vN R%                  5       n	U	(       a  U	 V
s/ sH  oR&                  PM     nn
UR                  U" U5      R!                  UR(                  R+                  U5      5      5      I S	h  vN   U	 H  n
UR                  U
5      I S	h  vN   M     UR-                  5       I S	h  vN   / n[/        U5       GH  u  pU[1        UR2                  5      :  a  UR2                  U   OS	nU(       a1  [1        UR4                  5      [1        US
   5      :  a  UR4                  OUR6                  nU(       a  UR8                  (       a  UR8                  OSUS-    3n0 n[/        US
   5       H*  u  nnU[1        U5      :  a    OUU   nSSUS   S.S.UU'   M,     [        [;        [<        R>                  " 5       5      U U[@        RB                  " US	[1        US
   5       SS9[D        RF                  S9nU" US/SUS    3/SSS0S.U[I        US   5      US   US   US   US
   S.	5        URK                  U5        URM                  U5        GM     URO                  5       I S	h  vN   U H  n
URQ                  U
5      I S	h  vN   M     [R        RT                  " U 5      I S	h  vN   SSK+J,n  SU [1        U5      UU V
s/ sH  n
U" U
5      PM     sn
S .$  GN# GNs  sn
f  GNr GNW GN> N Ni NIs  sn
f 7f)!zParse interaction JSON, create a room and one draft poll per branch.

Each poll is pre-configured with the correct trigger time, voting duration,
pause action, and winner jump actions derived from the JSON hotspots.
r   r   r   )ensure_roomset_poll_configr   r   r   z6No branch decision points detected in interaction JSONNoptionsu   劇情選擇 rj   postmessagejumpposition)commandr   )r/   ry   F)ensure_ascii)r   r[   r8   options_jsonr   rE   zcurrentTime>=r9   r   pauseduration_secondsend_timedefault_jump_time)	trigger_namestrigger_keyspause_actionwinner_actionsr   branch_start_timebranch_end_timebranch_default_jumpbranch_options)poll_outT)rn   r[   r   r   polls)-r   r   r   r   app.services.votingr   r   r   r   r   r   r   r   r   r   r   r   r[   r   r   r\   r   flush	enumeraterx   r   r   r   r8   rJ   uuiduuid4jsondumpsr   draftintaddrw   r   refreshr   r   app.routers.pollsr   )r[   rg   r   r   r   r   r   r   r   r   r   r   created_pollsidxbranch
branch_cfgr   r8   r   jopt_infor4   pollr   s                           rW   setup_from_interactionsr      s     /(@((,,^R@&--C!MNN./HC!YZZ
g
000llt4<<723 
su  "*+(QDD(+ll9T?001A1A(1KLMMMA..### mmoM *+.T]]1C+CT]]3'
 c*"9"9:c&BS>TT ##** 	 j..  q	* 	 *,$VI%67KAxC%%?D%% ( 4$N4 	 8 4::< 5s6)#456U ##
 	&Z,VL-A,BCD%%w/ - #F+=$> ?!'!5%j1#)*=#>$Y/
 	 	DT"e +h ..
ooa     
+
+G
444* H'45}!(1+}5 S 1 ,M#n  4 6s   A<O >N!?AO N$O "N'6AO :N,;O N/O 4N25F:O /N50O N7!O /N90O N;O $O 'O /O 2O 5O 7O 9O ;O )rg   rG   )rg   rY   )r   r   r[   rJ   r\   rJ   r`   rJ   )N)r   z4Annotated[Optional[str], Header(alias='X-Host-Key')])r[   rJ   r   
SessionDep)rg   r   )r[   rJ   rg   r   r   r   )>
__future__r   r   r   collectionsr   r   typingr   r   r   r	   r
   fastapir   r   r   r   r   r   pydanticr   r   sqlmodelr   sqlmodel.ext.asyncio.sessionr   
app.configr   app.dbr   r   r   r   app.routersr   r   app.servicesr   r   
voting_svc!app.services.interaction_scheduler   routerr   rv   rr   r}   rG   rY   postrh   rz   r   r~   r   r   r   r   r   r   r   rO   rV   rW   <module>r      s   "     7 7 L L %  5   0 ' < <	)8*	5|W[%99:
S!
 M
 ;Z
 F
  =
" HK+
 CU#
 @M!
 CE$
 ^7q< ~.y .
.Y 
. Y  _  N  "#raC()<=)<=B,=>	  	 $42	 2
9  HL^D^ +,-  8	8$ "#M $M .+,-  ee
e e	erV   