
    fi5                        d Z ddlmZ ddlZddlZddlZddlZddlmZ ddl	Z	ddl
mZ  ej                  d      ZdZdZd	Zdd
ZddZddZddZddZdddZddZy)u  
LINE Messaging API 客戶端(階段 6)。

這個模組是「跟 LINE 講話」的所有出口:
  1. verify_signature  ─ 驗 webhook 是不是真的來自 LINE
  2. reply_text         ─ 用 reply token 回應(免費,但 reply token 1 分鐘會過期)
  3. push_text          ─ 主動推送(用得到 to 是 userId/groupId/roomId,有 freemium 額度)
  4. get_profile        ─ 抓使用者顯示名稱與頭像
  5. get_message_content ─ 下載 image/audio/file 的二進位

設計原則:
  - 沒設 LINE_CHANNEL_ACCESS_TOKEN 就 silent skip(讓本機開發環境跑得起來)
  - 失敗只 log 不 raise(避免單一 LINE 訊息失敗就把整個 webhook handler 拖垮)
    )annotationsN)Any)settingszcoba.line.clientzhttps://api.line.me/v2/botzhttps://api-data.line.me/v2/boti$  c                 .    ddt         j                   dS )Nzapplication/jsonBearer )zContent-TypeAuthorization)r   LINE_CHANNEL_ACCESS_TOKEN     </volume1/homes/robertsu/coba/app/app/services/line_client.py_bearer_headersr   &   s     *"8#E#E"FG r   c                X   t         j                  r|syt        j                  t         j                  j	                  d      | t
        j                        j                         }t        j                  |      j                  d      }t        j                  ||j                               S )u!  LINE webhook 簽章驗證(HMAC-SHA256 + Base64)。

    body  : 原始 request body bytes(不能用 json 字串重新序列化,要 raw bytes)
    x_line_signature: HTTP header 'X-Line-Signature' 的值

    沒設 LINE_CHANNEL_SECRET 一律不通過(避免測試環境誤開後門)。
    Fzutf-8ascii)r   LINE_CHANNEL_SECREThmacnewencodehashlibsha256digestbase64	b64encodedecodecompare_digeststrip)bodyx_line_signaturemacexpecteds       r   verify_signaturer    -   s     ''/?
(($$++G4 fh	 
 $++G4Hx)9)?)?)ABBr   c                H    t        |       t        k  r| S | d t        dz
   dz   S )N   u   …)len	_TEXT_MAX)texts    r   	_truncater&   @   s)    
4yI)a- 5((r   c                  K   ddl }|j                  j                  d      dk(  rt        j	                  d|dd        yt
        j                  st        j                  d       y	| d
t        |      dgd}	 t        j                  d      4 d{   }|j                  t         d|t                      d{   }ddd      d{    j                  dk\  r,t        j                  d|j                  |j                          y	y7 {7 R7 D# 1 d{  7  sw Y   TxY w# t"        $ r t        j%                  d       Y y	w xY ww)u   用 reply token 回應。回 True 代表送出成功。

    reply token 1 分鐘內有效,所以這個只適合「立即回」的場景。
    Cooper 想 LLM 一輪才回的話用 push_text。
    r   NLINE_NO_PUSH1z[NO_PUSH] reply skipped: %sP   Tu2   LINE_CHANNEL_ACCESS_TOKEN 沒設,跳過 reply_textFr%   typer%   )
replyTokenmessages      $@timeoutz/message/replyjsonheaders,  u   LINE reply 失敗 %s: %su   LINE reply 例外osenvirongetloggerinfor   r	   warningr&   httpxAsyncClientpostLINE_API_BASEr   status_codeerrorr%   	Exception	exception)reply_tokenr%   r7   payloadcrs         r   
reply_textrI   F   s     	zz~~n%,149=--KL!$io>?G	$$T22affn=GUdUffggA 32==CLL3Q]]AFFK 3g 3222  ,-s   A5E8D) DD) 'D=D>DD) D>D) ED) DD) D&DD&"D) )E
E	E

Ec                  K   ddl }|j                  j                  d      dk(  rt        j	                  d| dd |dd        yt
        j                  st        j                  d	       y
| dt        |      dgd}	 t        j                  d      4 d{   }|j                  t         d|t                      d{   }ddd      d{    j                  dk\  r,t        j                  d|j                  |j                          y
y7 {7 R7 D# 1 d{  7  sw Y   TxY w# t"        $ r t        j%                  d       Y y
w xY ww)u/   主動推送(to = userId / groupId / roomId)。r   Nr(   r)   z [NO_PUSH] push skipped to=%s: %s   r*   Tu1   LINE_CHANNEL_ACCESS_TOKEN 沒設,跳過 push_textFr%   r+   )tor.   r/   r0   z/message/pushr2   r5   u   LINE push 失敗 %s: %su   LINE push 例外r6   )rL   r%   r7   rF   rG   rH   s         r   	push_textrM   c   s    	zz~~n%,63BcrK--JK$io>?G	$$T22affm<7TcTefffA 32==CLL2AMM166J 3f 3222  +,s   A9E<D- DD- 'DDDD- D>D- ED- DD- D*D!D*&D- -EEEEc                
  K   t         j                  sy|rt         d| d|  }nt         d|  }	 t        j                  d      4 d{   }|j                  |t                      d{   }ddd      d{    j                  dk(  r|j                         S t        j                  d	| |j                         y7 x7 W7 I# 1 d{  7  sw Y   YxY w# t        $ r t        j                  d
       Y yw xY ww)ux   抓 LINE 使用者顯示名 / 頭像。

    在群組裡要用 /group/{groupId}/member/{userId},不然會失敗。
    Nz/group/z/member/z	/profile/r/   r0   r4      u   LINE profile 抓不到 %s: %su   LINE profile 例外)r   r	   r@   r=   r>   r9   r   rA   r3   r:   r<   rC   rD   )user_idgroup_idurlrG   rH   s        r   get_profilerT   |   s     
 --wxj	By	2	$$T22aeeC):e;;A 32==C668O6O 3; 3222  ./s   .DC CC C
.C/C
3C >C?"C !D"!C DC C
C 
CCCC D =D?D  Dc                  K   t         j                  syt         d|  d}	 t        j                  d      4 d{   }|j                  |ddt         j                   i       d{   }ddd      d{    j                  d	k(  r|j                  S t        j                  d
|j                  |j                         y7 7 ]7 O# 1 d{  7  sw Y   _xY w# t        $ r t        j                  d       Y yw xY ww)u   下載 image / audio / video / file 訊息的二進位內容。

    注意 host 是 api-data.line.me 不是 api.line.me。
    Nz	/message/z/contentg      >@r0   r   r   rO   rP   u    LINE content 下載失敗 %s: %su   LINE content 下載例外)r   r	   LINE_DATA_API_BASEr=   r>   r9   rA   contentr:   rB   r%   rC   rD   )
message_idrS   rG   rH   s       r   get_message_contentrY      s     
 -- 	*X
>C	$$T22aeeC/WXEgEgDh;i)jekkA 32==C997O 3k 3222  45s   DC  CC  *C)C*C.C  9C	:C  D+C  DC  C	C  CCCC   D>D DD)returnzdict[str, str])r   bytesr   
str | NonerZ   bool)r%   strrZ   r^   )rE   r^   r%   r^   rZ   r]   )rL   r^   r%   r^   rZ   r]   )N)rQ   r^   rR   r\   rZ   zdict[str, Any] | None)rX   r^   rZ   zbytes | None)__doc__
__future__r   r   r   r   loggingtypingr   r=   
app.configr   	getLoggerr:   r@   rV   r$   r   r    r&   rI   rM   rT   rY   r
   r   r   <module>re      so    #        
		-	. -6 	C&):2.r   