
    i                    &   d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	 ddl
mZ ddlmZmZmZmZmZmZ dd	lmZmZ dd
lmZ  ej        e          Z G d de          Z	 ddlZn# eef$ r dZY nw xY wd dZ efd!dZ!efd"dZ"d#dZ#dZ$d$d%dZ%dS )&u\  
QQBot scan-to-configure (QR code onboard) module.

Mirrors the Feishu onboarding pattern: synchronous HTTP + a single public
entry-point ``qr_register()`` that handles the full flow (create task →
display QR code → poll → decrypt credentials).

Calls the ``q.qq.com`` ``create_bind_task`` / ``poll_bind_result`` APIs to
generate a QR-code URL and poll for scan completion.  On success the caller
receives the bot's *app_id*, *client_secret* (decrypted locally), and the
scanner's *user_openid* — enough to fully configure the QQBot gateway.

Reference: https://bot.q.qq.com/wiki/develop/api-v2/
    )annotationsN)IntEnum)OptionalTuple)quote   )ONBOARD_API_TIMEOUTONBOARD_CREATE_PATHONBOARD_POLL_INTERVALONBOARD_POLL_PATHPORTAL_HOSTQR_URL_TEMPLATE)decrypt_secretgenerate_bind_key)get_api_headersc                  "    e Zd ZdZdZdZdZdZdS )
BindStatusz/Status codes returned by ``_poll_bind_result``.r   r         N)__name__
__module____qualname____doc__NONEPENDING	COMPLETEDEXPIRED     D/home/ubuntu/.hermes/hermes-agent/gateway/platforms/qqbot/onboard.pyr   r   +   s(        99DGIGGGr   r   urlstrreturnboolc                   t           dS 	 t          j        t           j        j        d          }|                    |            |                    d           |                    d           dS # t          $ r Y dS w xY w)zDTry to render a QR code in the terminal. Returns True if successful.NFr   )error_correctionborderT)fit)invert)_qrcode_modQRCode	constantsERROR_CORRECT_Madd_datamakeprint_ascii	Exception)r!   qrs     r    
_render_qrr3   >   s    u
(2B
 
 
 	C
D
d###t   uus   A&A3 3
B BtimeoutfloatTuple[str, str]c                l   ddl }dt           t           }t                      }|                    | d          5 }|                    |d|it                                }|                                 |                                }ddd           n# 1 swxY w Y   |	                    d          dk    r#t          |	                    d	d
                    |	                    di           	                    d          }|st          d          t                              d|           ||fS )zCreate a bind task and return *(task_id, aes_key_base64)*.

    Raises:
        RuntimeError: If the API returns a non-zero ``retcode``.
    r   Nhttps://Tr4   follow_redirectskeyjsonheadersretcodemsgzcreate_bind_task faileddatatask_idz-create_bind_task: missing task_id in responsezcreate_bind_task ok: task_id=%s)httpxr   r
   r   Clientpostr   raise_for_statusr=   getRuntimeErrorloggerdebug)r4   rC   r!   r;   clientresprA   rB   s           r    _create_bind_taskrM   T   sa    LLL
7[
7"5
7
7C


C	g	=	= {{3eS\?;L;L{MMyy{{              
 xx	a488E+DEEFFFhhvr""&&y11G LJKKK
LL2G<<<C<s   ABBBrB    Tuple[BindStatus, str, str, str]c                   ddl }dt           t           }|                    |d          5 }|                    |d| it                                }|                                 |                                }ddd           n# 1 swxY w Y   |                    d          dk    r#t          |                    d	d
                    |                    di           }t          |                    dd                    t          |                    dd                    |                    dd          |                    dd          fS )zPoll the bind result for *task_id*.

    Returns:
        A 4-tuple of ``(status, bot_appid, bot_encrypt_secret, user_openid)``.

    Raises:
        RuntimeError: If the API returns a non-zero ``retcode``.
    r   Nr8   Tr9   rB   r<   r?   r@   zpoll_bind_result failedrA   status	bot_appid bot_encrypt_secretuser_openid)rC   r   r   rD   rE   r   rF   r=   rG   rH   r   r"   )rB   r4   rC   r!   rK   rL   rA   ds           r    _poll_bind_resultrV   o   sq    LLL
5[
5"3
5
5C	g	=	= {{3i%9?CTCT{UUyy{{              
 xx	a488E+DEEFFFA1551%%&&AEE+r""##	"B''	mR  	 s   ABBBc                F    t          j        t          |                     S )z3Build the QR-code target URL for a given *task_id*.rB   )r   formatr   rX   s    r    build_connect_urlrZ      s    !%..9999r   r   X  timeout_secondsintOptional[dict]c                   t          j                    | z   }t          t          dz             D ]}	 t	                      \  }}n4# t
          $ r'}t                              d|           Y d}~ dS d}~ww xY wt          |          }t                       t          |          rt          d|            n!t          d|            t          d           t                       t          j                    |k     r*	 t          |          \  }}}	}
n)# t
          $ r t          j        t                     Y Sw xY w|t          j        k    rMt!          |	|          }t                       t          d| d           |
rt          d	|
            |||
d
c S |t          j        k    rM|t          k    r#t                              dt                      dS t          d|dz    dt           d           nOt          j        t                     t          j                    |k     *t                              d|             dS dS )ug  Run the QQBot scan-to-configure QR registration flow.

    Mirrors ``feishu.qr_register()``: handles create → display → poll →
    decrypt in one call.  Unexpected errors propagate to the caller.

    :returns:
        ``{"app_id": ..., "client_secret": ..., "user_openid": ...}`` on
        success, or ``None`` on failure / expiry / cancellation.
    r   z.[QQBot onboard] Failed to create bind task: %sNz7  Scan the QR code above, or open this URL directly:
  z'  Open this URL in QQ on your phone:
  z>  Tip: pip install qrcode  to display a scannable QR code herez  QR scan complete! (App ID: )z  Scanner's OpenID: )app_idclient_secretrT   u6   [QQBot onboard] QR code expired %d times — giving upz#
  QR code expired, refreshing... (/z([QQBot onboard] Poll timed out after %ds)time	monotonicrange_MAX_REFRESHESrM   r1   rI   warningrZ   printr3   rV   sleepr   r   r   r   r   )r\   deadlinerefresh_countrB   aes_keyexcr!   rP   ra   encrypted_secretrT   rb   s               r    qr_registerrp      s    ~/1H~122 2 2	022GWW 	 	 	NNKSQQQ444444	  (( 	c?? 	TRSRRSSSSBSBBCCCRSSS n))@QRY@Z@Z= 0++   
0111 --- ./? I I?f???@@@ @>>>???$%2#.     +++ N22NN#[]klll44b]Q=NbbQ_bbbcccJ,---5 n)): NNEWWW44  4s)   A
A5A00A5?D#D:9D:)r!   r"   r#   r$   )r4   r5   r#   r6   )rB   r"   r4   r5   r#   rN   )rB   r"   r#   r"   )r[   )r\   r]   r#   r^   )&r   
__future__r   loggingrd   enumr   typingr   r   urllib.parser   r,   r	   r
   r   r   r   r   cryptor   r   utilsr   	getLoggerr   rI   r   qrcoder*   ImportError	TypeErrorr3   rM   rV   rZ   rg   rp   r   r   r    <module>r|      s    # " " " " "         " " " " " " " "                      6 5 5 5 5 5 5 5 " " " " " "		8	$	$            Y   KKK   , (;     : )    B: : : : @ @ @ @ @ @ @s   "A' '	A32A3