a
    Tirr                  )   @   s6  U d Z ddlZddlZddlZddlmZmZmZmZm	Z	 ddl
mZmZmZmZmZ ddlmZ eeZG dd deZe	eeddd	d
Ze	eeddddZe	eeddddZe	eeddddZe	eeddddZe	eeddddZe	eeddddZe	eeddddZe	eeddddZe	eeddddZe	eeddddZe	eedddd Z e	eeddd!d"Z!e	eeddd#d$Z"e	eeddd%d&Z#e	eeddd'd(Z$e	eeddd)d*Z%e	eeddd+d,Z&e	eeddd-d.Z'e	eeddd/d0Z(e	eeddd1d2Z)e	eeddd3d4Z*e	eeddd5d6Z+e	eeddd7d8Z,e	eeddd9d:Z-e	eeddd;d<Z.e	eeddd=d>Z/e	eeddd?d@Z0e	eedddAdBZ1e	eedddCdDZ2e	eedddEdFZ3e	eedddGdHZ4e	eedddIdJZ5e	eedddKdLZ6e	eedddMdNZ7e	eedddOdPZ8e	eedddQdRZ9e	eedddSdTZ:e	eedddUdVZ;e	eedddWdXZ<e=e=eddYdZd[Z>eeeeeeeeeeee e9e!e"e#e$e%e&e'e(e)e*e+e,e-e.e/e0e1e2e3e4e5e6e7e7e8e:e;d\(Z?e	e=ef e@d]< e	e=ef eeddd^d_ZAdS )`z
Command Dispatcher - Table-driven command handling for WebSocket events.

This module extracts all command handling logic from the consumer() function
into a clean, maintainable, O(1) lookup-based dispatcher pattern.
    N)ProtocolCallableAnyOptionalDict)sendDictsendUserInfosendEventMessage
sendToUserComplexHandler)configc                   @   s,   e Zd ZdZeeef eeddddZdS )CommandHandlerz5Protocol defining the interface for command handlers.Neventplayer	websocketreturnc                    s   dS )zHandle a command event.

        Args:
            event: The parsed JSON event from the client
            player: The player object from playerRecords
            websocket: The WebSocket connection
        N )selfr   r   r   r   r   :/var/www/lichun.app/lichun/ws/server/command_dispatcher.py__call__   s    zCommandHandler.__call__)__name__
__module____qualname____doc__r   strr   r   r   r   r   r   r      s   r   r   c                    s"   t ||I dH  td d|_dS )zHandle game stop command.Nzstopped!inactive)r   print
controllerr   r   r   r   r   r   handle_stop)   s    r    c                    s   t d d|_dS )zHandle game start command.zstarted!activeN)r   r   r   r   r   r   handle_start0   s    r"   c                    s`   ddl m}m} ddl}td | }| |_|j|_|j	|j| t
||I dH  d|_dS )zHandle game restart command.r   )playerClassgetOccupationsNzrestart!T)	functionsr#   r$   appr   occupationsuserIDidplayerRecordssetr   updateClient)r   r   r   r#   r$   r&   r   r   r   handle_restart6   s    r-   c                    sB   ddl m} ||| d  t||I dH  t|jd|I dH  dS )zHandle character setup.r   )characterSetupmessageNZcharacter_created)r%   r.   r   _check_achievements_triggerr)   )r   r   r   r.   r   r   r   handle_character_setupD   s    r1   c                    s,   | d |_ td|j   t||I dH  dS )z!Handle device token registration.r/   zdeviceToken: N)deviceTokenr   r   r   r   r   r   handle_device_tokenM   s    
r3   c                    s   t |d|jdI dH  dS )z$Handle get extracurriculars request.extraCurriculars)typer4   N)r   r4   r   r   r   r   handle_get_extracurricularsT   s    r6   c                    s:   ddl m} |jd ||| d  t||I dH  dS )z!Handle apply for extracurricular.r   )applyForExtracurricularextracurricularr/   N)r%   r7   askedQuestionsaddr   )r   r   r   r7   r   r   r    handle_apply_for_extracurricular\   s    r;   c                    s.   ddl m} ||| d  t||I dH  dS )zHandle quit extracurricular.r   )quitExtraCurricularr/   N)r%   r<   r   )r   r   r   r<   r   r   r   handle_quit_extracurriculare   s    r=   c                    sf   ddl m}m} ddlm} ||| d }|||I dH |_|||_t|tj	|j
tdI dH  dS )zHandle retrieve person request.r   )
get_persongetPersonDescription)parseConversationsr/   N)default)r%   r>   r?   conversationEventsr@   availableConversationsdescriptionr
   jsondumps__dict__r   )r   r   r   r>   r?   r@   personr   r   r   handle_retrieve_personm   s    
rI   c           	         s\  ddl m} ddlm}m} | d } |j|_tj|_|j	j
|jkrL|j	 j|j8  _||j	 t|d|j	j
dI dH  t| d | d	  d}|jD ]}|j| d
 kr|} qq||| d
 }|du s|j|jkr|td | ddsd| d< td ntd| d   ||| d
 | d	 | d dI dH }td|j  t|tj|jtddI dH  t|tj|jtddI dH  n| d dks| d dkrXtd |j| d |j	j|j|jd | jd7  _||| d
 | d	 | d dI dH }t|jd j td|j  t|tj|jtddI dH  t|tj|jtddI dH  n|jd dS )zHandle conversation event.r   )conversationInit)r>   getPeakEnergyr/   u)r5   
calcEnergyNconversationEventcTypecharacterIDzCno conversation found for this character, creating new conversationresponseFzNo responsez
response: )r   	characterrO   rQ   z!Sending new conversation to user )rA   ensure_asciiZfreeResponse)datetime   z$Sending conversation update to user z/You don't have enough energy to talk right now.) rB   rJ   r%   r>   rK   	gameSpeedpreviousGameSpeedr   SPEED_PAUSEDcrM   messageEnergyCostenergyr   r   conversationsrR   r)   getr
   rE   rF   rG   r   
addMessagerT   rU   questionconversationr/   messageQueueappend)	r   r   r   rJ   r>   rK   convrb   rR   r   r   r   handle_conversationz   sP    



 
 rf   c                    s   ddl m} ||| d  dS )z!Handle mark conversation as read.r   )markConversationAsReadr/   N)r%   rg   )r   r   r   rg   r   r   r    handle_mark_conversation_as_read   s    rh   c                    s8  ddl m}m}m} | }|j}||_| d dkr||v r||}|d }	|	dkrt||	 |_||j||jdd qt	d|  n^t
d	| d
 t|D ]B\}
}||kr|
dkr|n
||
d  |_||j||jdd  qqn4| d dkr||v rR||}|d }	|	t|k r@||	 |_||j||jdd nt	d|  nPt
d	| d
 t|D ]4\}
}||krl||_||j||jdd  qqlnx| d dkr|jtjks|jtjkrtj|_n|j|_||j||jdd n&| d }|||_||j||jdd t|d|jdI dH  dS )zHandle speed change command.r   )get_speed_button_valuesvalidate_game_speedlog_speed_changer/   -rV   z	button(-))sourcez'Speed already at minimum button value: zSpeed z& not in button values, finding nearest+z	button(+)z'Speed already at maximum button value: resetZdirectrL   )r5   rX   N)r%   ri   rj   rk   rX   rY   indexr(   loggerdebugwarning	enumeratelenr   rZ   SPEED_QUESTION_PAUSESPEED_DEFAULTr   )r   r   r   ri   rj   rk   ZbuttonSpeedValues	old_speedcurrent_indexZ
next_indexiZ	btn_speedZrequested_speedr   r   r   handle_speed   sV    









r{   c                    s<   ddl m} ||| d d | d d  t||I dH  dS )zHandle focus update.r   )update_focusr/   Z
activityIdZnewFocusN)r%   r|   r   )r   r   r   r|   r   r   r   handle_focus_update   s    r}   c                    s.   ddl m} ||| d  t||I dH  dS )zHandle quit habit.r   )	quitHabitr/   N)r%   r~   r   )r   r   r   r~   r   r   r   handle_quit_habit   s    r   c                    s.   ddl m} ||| d  t||I dH  dS )zHandle stop quit habit.r   )stopQuitHabitr/   N)r%   r   r   )r   r   r   r   r   r   r   handle_stop_quit_habit  s    r   c                    s.   ddl m} ||| d  t||I dH  dS )zHandle purchase item.r   )purchaseItemr/   N)r%   r   r   )r   r   r   r   r   r   r   handle_purchase_item  s    r   c                    s.   ddl m} ||| d  t||I dH  dS )zHandle purchase in-app item.r   )purchaseInAppItemr/   N)r%   r   r   )r   r   r   r   r   r   r   handle_purchase_in_app_item  s    r   c                    sD   ddl m}  fdd}||j| di |d t| I dH  dS )zHandle purchase energy refill.r   )handle_purchase_energy_refillc                    s   t t | d S Nasynciocreate_taskr   	player_idr/   r   r   r   send_to_client_wrapper!  s    z=handle_purchase_energy_refill.<locals>.send_to_client_wrapperr/   r   message_datasend_to_clientN)monetization.energy_refillsr   r)   r_   r   )r   r   r   r   r   r   r   r   r     s    
r   c                    sD   ddl m}  fdd}||j| di |d t| I dH  dS )zHandle purchase time skip.r   )handle_purchase_time_skipc                    s   t t | d S r   r   r   r   r   r   r   0  s    z9handle_purchase_time_skip.<locals>.send_to_client_wrapperr/   r   N)monetization.time_skipsr   r)   r_   r   )r   r   r   r   r   r   r   r   r   ,  s    
r   c                    s,   ddl m} | }t|d|dI dH  dS )zHandle get energy refill tiers.r   )get_refill_tiersZenergyRefillTiersr5   tiersN)r   r   r   )r   r   r   r   r   r   r   r   handle_get_energy_refill_tiers;  s    r   c                    s,   ddl m} | }t|d|dI dH  dS )zHandle get time skip tiers.r   )get_skip_tiersZtimeSkipTiersr   N)r   r   r   )r   r   r   r   r   r   r   r   handle_get_time_skip_tiersC  s    r   c                    s0   ddl m} ||j}t|d|dI dH  dS )zHandle get achievements.r   )get_player_achievementsZachievementsList)r5   achievementsN)retention.achievementsr   r)   r   )r   r   r   r   r   r   r   r   handle_get_achievementsK  s    
r   c                    s0   |  di  d}|r,td|j d|  dS )zHandle acknowledge achievement.r/   ZachievementIdzPlayer z acknowledged achievement N)r_   r   r)   )r   r   r   Zachievement_idr   r   r   handle_acknowledge_achievementS  s    r   c                    s(   ddl m}  fdd}||j| dS )zHandle get daily rewards.r   )handle_daily_login_checkc                    s   t t | d S r   r   r   r   r   r   r   _  s    z8handle_get_daily_rewards.<locals>.send_to_client_wrapperN)retention.daily_rewardsr   r)   )r   r   r   r   r   r   r   r   handle_get_daily_rewards[  s    r   c                    sT   ddl m} | di dd}||j}t|d|dI dH  t||I dH  dS )	zHandle claim daily reward.r   )claim_daily_rewardr/   dayrV   ZdailyRewardClaimedr5   resultN)r   r   r_   r)   r   r   )r   r   r   r   r   r   r   r   r   handle_claim_daily_rewarde  s
    
r   c                    s(   ddl m}  fdd}||j| dS )zHandle get daily quests.r   )handle_daily_quest_checkc                    s   t t | d S r   r   r   r   r   r   r   s  s    z7handle_get_daily_quests.<locals>.send_to_client_wrapperN)retention.daily_questsr   r)   )r   r   r   r   r   r   r   r   handle_get_daily_questso  s    r   c                    s|   ddl m}m} | di d}|rx||j|}t d|dI dH  |d rxt| I dH   fd	d
}||j| dS )zHandle claim quest reward.r   )claim_quest_rewardr   r/   ZquestIdZquestRewardClaimedr   Nsuccessc                    s   t t | d S r   r   r   r   r   r   r     s    z9handle_claim_quest_reward.<locals>.send_to_client_wrapper)r   r   r   r_   r)   r   r   )r   r   r   r   r   quest_idr   r   r   r   r   handle_claim_quest_rewardy  s    r   c                    s0   ddl m} ||j}t|d|dI dH  dS )zHandle get player statistics.r   )get_player_statisticsZplayerStatistics)r5   
statisticsN)Zretention.statisticsr   r)   r   )r   r   r   r   statsr   r   r   handle_get_player_statistics  s    
r   c           	   
      s   ddl m} ddlm} d}zz6| }||d}||j}t|d|dI dH  W nP ty } z8t	d|  t|d	d
t
|dI dH  W Y d}~n
d}~0 0 W |r|  n|r|  0 dS )zHandle data export.r   DataManagementServiceget_database_connectionNZdb_connectionZdataExportComplete)r5   datazData export error: errorZEXPORT_FAILEDr5   
error_coder/   )api.data_managementr   databaser   Zexport_player_datar)   r   	Exceptionloggingr   r   close)	r   r   r   r   r   connserviceZexport_dataer   r   r   handle_export_data  s,    
&
r   c           
   
      s   ddl m} ddlm} | di dd}|dkrNt|dd	d
dI dH  nd}zz8| }||d}||j}t|dd|dI dH  W nP ty }	 z8t	
d|	  t|ddt|	dI dH  W Y d}	~	n
d}	~	0 0 W |r|  n|r|  0 dS )zHandle account deletion.r   r   r   r/   confirmation DELETEr   ZINVALID_CONFIRMATIONzMust type DELETE to confirmr   Nr   ZaccountDeletionScheduled   )r5   ZgracePeriodDaysr   zAccount deletion error: ZDELETION_FAILED)r   r   r   r   r_   r   Zdelete_player_accountr)   r   r   r   r   r   )
r   r   r   r   r   r   r   r   r   r   r   r   r   handle_delete_account  s<    
&
r   c                    sB   ddl m} ||| d  t||I dH  t|jd|I dH  dS )zHandle apply for job.r   )applyForJobr/   NZjob_applied)r%   r   r   r0   r)   )r   r   r   r   r   r   r   handle_apply_for_job  s    r   c                    s.   ddl m} ||| d  t||I dH  dS )zHandle quit job.r   )quitJobr/   N)r%   r   r   )r   r   r   r   r   r   r   handle_quit_job  s    r   c                    sB   ddl m} ||| d  t||I dH  t|jd|I dH  dS )zHandle romance action.r   )romancer/   NZrelationship_started)r%   r   r   r0   r)   )r   r   r   r   r   r   r   handle_romance  s    r   c                    sT   ddl m} ||| d }|dur4t||I dH  ndddd	}t||I dH  dS )
zHandle date night.r   )	dateNightr/   FNr   Date Night Failedz.Not enough energy or money for the date night.r5   titler/   )r%   r   r	   )r   r   r   r   r   failure_messager   r   r   handle_date_night  s    r   c                    s.   ddl m} ||| d  t||I dH  dS )zHandle break up or divorce.r   )breakUpr/   N)r%   r   r   )r   r   r   r   r   r   r   handle_break_up  s    r   c                    sT   ddl m} ||| d }|dur4t||I dH  ndddd	}t||I dH  dS )
zHandle partner gift.r   )partnerGiftr/   FNr   r   z(Not enough energy or money for the gift.r   )r%   r   r	   )r   r   r   r   r   r   r   r   r   handle_partner_gift   s    r   c                    s   |j |_dS )zHandle reset speed.N)rY   rX   r   r   r   r   handle_reset_speed  s    r   c                    sb   |j jd d|j _|jd|j j d |j j d  |j  jd7  _|j|j  d|_ dS )	zHandle swipe match.dating_matchzDating MatchzYou matched with  !(   FN)	swipeCharacterrelationshipsrd   r   rc   	firstnamelastnameaffinityrr   r   r   r   handle_swipe_match  s    $r   c                    s   ddl m} ddl}|jjdkr$dnd}|||ddd}d	}td
|jj| }|jj| }	|||	}
|
|_||_t	d|j
 d |j  t|| d |dI dH  dS )zHandle get swipe character.r   )create_characterNFemaleMalerandom_adultsnone)agerelationship      zsending character: r   r5   )r5   r   )r%   r   randomr[   sexmaxageYearsrandintr   r   r   r   r   )r   r   r   r   r   r   rR   Zage_range_widthZlower_boundZupper_boundZ
random_ager   r   r   handle_get_swipe_character  s    r   c              
      s  ddl m}m} ddlm} ddlm} d| d< d| d v rh| d dd	 | d< | d dd | d< |jj	}|jj
}|jj}	zd
| v rt| d
 tr| d
 }
d|
v r|j j	|
d 8  _	d|
v r|j j
|
d 8  _
d|
v r|j j|
d 8  _|| d |d| d| d
 d|_|j|_|j| d  W n |y } zX||j_	||j_
|	|j_td|  ddlm} ||d| d  I dH  W Y d}~nd}~0  ty$ } zf||j_	||j_
|	|j_td|  tjd| d  dd ddlm} ||dI dH  W Y d}~n
d}~0 0 |jrt|jdkr|jd|_|j|j td|j  |||d|j|dI dH  d|_dS )zBHandle generic event with event handler system and cost deduction.r   )call_event_handlerInvalidEventError)r	   )messageFunctionFkeyz---r5   rV   r/   
energyCost	moneyCostdiamondCostanswerTzInvalid event rejected: )r   zInvalid event type: NzEvent handler error: zHandler crashed: )exc_infoz)An error occurred processing your requestzsending message from queue ZanswerQueuer   )event_handlersr   r   server.websocket_messagingr	   r%   r   splitr[   r]   moneydiamonds
isinstancedictr_   r,   rY   rX   r9   r:   r   r&   r   r   rq   rc   ru   popr/   
messageLogrd   )r   r   r   r   r   r	   r   Zoriginal_energyZoriginal_moneyZoriginal_diamondsr/   r   r   r   r   r   handle_generic_event4  sX     .&r  )r   
event_typer   r   c                    sD   ddl m}  fdd}|| |}|r@|D ]}|| d|d q*dS )z*Auto-check achievements when events occur.r   )check_achievementsc                    s   t t | d S r   r   )pidr/   r   r   r   r   w  s    z;_check_achievements_trigger.<locals>.send_to_client_wrapperZachievementUnlocked)r5   achievementN)r   r	  )r   r  r   r	  r   Zunlocked_achievementsr  r   r   r   r0   s  s    
r0   )(stopstartrestartr.   r2   getExtraCurricularsr7   ZquitExtracurricularZretrievePersonrb   rg   speedZ
resetSpeedZfocusUpdater~   r   r   r   ZpurchaseEnergyRefillZpurchaseTimeSkipZgetEnergyRefillTiersZgetTimeSkipTiersZgetAchievementsZacknowledgeAchievementZgetDailyRewardsZclaimDailyRewardZgetDailyQuestsZclaimQuestRewardZgetPlayerStatisticsZ
exportDataZdeleteAccountr   r   r   r   r   Zdivorcer   Z
swipeMatchZgetSwipeCharacterCOMMAND_REGISTRYc                    s|   d}|  ddv r| d }nd| v r,| d }|sDtd|   dS t |}|rf|| ||I dH  nt| ||I dH  dS )a  
    Dispatch a command to the appropriate handler using O(1) dictionary lookup.

    This function replaces the massive if/elif chain in consumer() with a clean,
    table-driven approach that's easier to maintain and extend.

    Args:
        event: The parsed JSON event from the client
        player: The player object from playerRecords
        websocket: The WebSocket connection

    Raises:
        Exception: Any exception raised by the handler
    Nr/   )r  r  r  r5   z No command type found in event: )r_   rq   rs   r  r  )r   r   r   Zcommand_typehandlerr   r   r   dispatch_command  s    

r  )Br   rE   r   r   typingr   r   r   r   r   r   r   r   r	   r
   r   r   	getLoggerr   rq   r   r    r"   r-   r1   r3   r6   r;   r=   rI   rf   rh   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   r0   r  __annotations__r  r   r   r   r   <module>   s   
		2B


#		
?O