§
    ‰eî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© ó    úA/home/div/.hermes/hermes-agent/gateway/platforms/qqbot/onboard.pyr   r   +   s(   € € € € € Ø9Ð9à€DØ€GØ€IØ€G€G€Gr   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øÝð ð ð Øˆuˆuðøøø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 )z�Create 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  € ð €L€L€Là
7•[Ð
7Õ"5Ð
7Ð
7€CÝ
Ñ
Ô
€Cà	�Š˜g¸ˆÑ	=Ô	=ð ÀØ�{Š{˜3 e¨S \½?Ñ;LÔ;Lˆ{ÑMÔMˆØ×ÒÑÔÐØ�yŠy‰{Œ{ˆðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð
 ‡x‚x�	ÑÔ˜aÒÐÝ˜4Ÿ8š8 EÐ+DÑEÔEÑFÔFÐFà�hŠh�v˜rÑ"Ô"×&Ò& yÑ1Ô1€GØð LÝÐJÑKÔKÐKå
‡L‚LÐ2°GÑ<Ô<Ð<Ø�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 )zÓPoll 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  € ð €L€L€Là
5•[Ð
5Õ"3Ð
5Ð
5€Cà	�Š˜g¸ˆÑ	=Ô	=ð ÀØ�{Š{˜3 i°Ð%9Å?ÑCTÔCTˆ{ÑUÔUˆØ×ÒÑÔÐØ�yŠy‰{Œ{ˆðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð
 ‡x‚x�	ÑÔ˜aÒÐÝ˜4Ÿ8š8 EÐ+DÑEÔEÑFÔFÐFà�Š�˜ÑÔ€Aå�1—5’5˜ 1Ñ%Ô%Ñ&Ô&ÝˆA�EŠE�+˜rÑ"Ô"Ñ#Ô#Ø	�ŠÐ" BÑ'Ô'Ø	�Šˆm˜RÑ Ô ð	ð 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›  € õ Œ~ÑÔ /Ñ1€Hå�~°Ñ1Ñ2Ô2ð 2ñ 2ˆð	Ý0Ñ2Ô2ÑˆG�W�WøÝð 	ð 	ð 	Ý�NŠNÐKÈSÑQÔQÐQØ�4�4�4�4�4�4øøøøð	øøøõ   Ñ(Ô(ˆõ 	‰ŒˆÝ�c‰?Œ?ð 	TÝÐRÈSÐRÐRÑSÔSÐSÐSåÐB¸SÐBÐBÑCÔCÐCÝÐRÑSÔSÐSÝ‰Œˆõ ŒnÑÔ Ò)Ñ)ðÝ@QÐRYÑ@ZÔ@ZÑ=�˜Ð 0°+°+øÝð ð ð Ý”
Õ0Ñ1Ô1Ð1Ø�ðøøøð �Ô-Ò-Ð-Ý .Ð/?ÀÑ IÔ I�Ý‘”�ÝÐ?°fÐ?Ð?Ð?Ñ@Ô@Ð@Øð @ÝÐ>°Ð>Ð>Ñ?Ô?Ð?à$Ø%2Ø#.ðð ð ð ð ð �Ô+Ò+Ð+Ø ¥NÒ2Ð2Ý—N’NÐ#[Õ]kÑlÔlÐlØ˜4˜4ÝÐb¸]ÈQÑ=NÐbÐbÕQ_ÐbÐbÐbÑcÔcÐcØåŒJÕ,Ñ-Ô-Ð-õ5 ŒnÑÔ Ò)Ñ)õ: �NŠNÐEÀÑWÔWÐWØ�4�4ñ ð ˆ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Ðð ð ð Ø€K€K€Kðøøøðð ð ð ð, (;ð ð ð ð ð ð: )ðð ð ð ð ðB:ð :ð :ð :ð €ð@ð @ð @ð @ð @ð @ð @s   Á"A' Á'	A3Á2A3