
    Gi                     Z    d Z ddlmZ ddlmZ ddlmZ ddlmZ  G d d      Z	 e	       Z
y)	u  
WebSocket 連線管理器(階段 3c)。

設計重點:
  - 單 process in-memory pub/sub(階段 3 用 SQLite 沒 Redis,夠 5-15 人團隊)
  - 同一個使用者可以開多個 WS(手機 + 電腦同時登入)
  - HTTP API 改完資料後 → 呼叫 manager.broadcast() → 所有連線收到事件
  - 客戶端自己 filter 事件相關性(channel_id 對得上才更新 UI)

未來 Stage 5 上 NAS 多 process 時,把這個換成 Redis Pub/Sub 即可,
HTTP API 那層的呼叫介面不用改。
    )defaultdict)Any)UUID)	WebSocketc                       e Zd ZdZddZdededdfdZdededdfdZ	 dd	e	e
ef   d
edz  ddfdZdefdZdee   fdZy)ConnectionManageru<   管理所有開著的 WebSocket 連線,提供廣播功能。returnNc                 ,    t        t              | _        y N)r   list_connectionsselfs    7/volume1/homes/robertsu/coba/app/app/core/ws_manager.py__init__zConnectionManager.__init__   s    9DT9J    	websocketuser_idc                 H   K   | j                   |   j                  |       yw)u?   接受連線並登記,呼叫前 caller 自己處理 accept()。N)r   appendr   r   r   s      r   connectzConnectionManager.connect   s     '")))4s    "c                     || j                   vry	 | j                   |   j                  |       | j                   |   s| j                   |= yy# t        $ r Y )w xY w)uD   從登記表移除一條連線(連線意外掉的時候被呼叫)。N)r   remove
ValueErrorr   s      r   
disconnectzConnectionManager.disconnect    sh    $+++	g&--i8   )!!'* *  		s   A 	AAeventexclude_user_idc                 J  K   g }t        | j                  j                               D ]7  \  }}|||k(  rt        |      D ]  }	 |j                  |       d{     9 |D ]  \  }}| j                  ||        y7 %# t        $ r |j                  ||f       Y ^w xY ww)uP  把事件送給所有(或除了 exclude_user_id 以外的)連線。

        各 endpoint 改資料後呼叫,例如:
            await manager.broadcast({"type": "message_new", "channel_id": "...", "message": {...}})

        傳送失敗的連線會被自動清掉(可能對方已關瀏覽器但伺服器還沒收到 close)。
        N)r   r   items	send_json	Exceptionr   r   )r   r   r   deadr   ws_listwss          r   	broadcastzConnectionManager.broadcast+   s      .0 $T%6%6%<%<%> ?GW*w//I7m/,,u--- $ !@  KGROOB(   .  /KK"./s<   AB#BA?B!B#?BB B#B  B#c                 V    t        d | j                  j                         D              S )u0   目前在線連線數(debug / monitoring 用)。c              3   2   K   | ]  }t        |        y wr   )len).0vs     r   	<genexpr>z5ConnectionManager.connection_count.<locals>.<genexpr>E   s     >#=a3q6#=s   )sumr   valuesr   s    r   connection_countz"ConnectionManager.connection_countC   s"    >4#4#4#;#;#=>>>r   c                 H    t        | j                  j                               S )u"   目前在線使用者 ID 集合。)setr   keysr   s    r   online_user_idsz!ConnectionManager.online_user_idsG   s    4$$))+,,r   )r	   Nr   )__name__
__module____qualname____doc__r   r   r   r   r   dictstrr   r&   intr/   r1   r3    r   r   r   r      s    FK5y 54 5D 5	+I 	+ 	+ 	+ (,)CH~) ) 
	)0?# ?-T -r   r   N)r7   collectionsr   typingr   uuidr   fastapir   r   managerr;   r   r   <module>rA      s-    $   4- 4-p 
r   