
    `gjĚ                        d Z ddlZddl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	m	Z	 ddl
mZmZmZmZ ddlmZ ddlZddlmZmZ ddlmZmZmZmZmZmZmZ dd	lmZ  ej>                  e       Z!d
Z"dZ#dZ$dZ%dZ&ddgZ'dddddddZ(dddddddZ)h dZ* ejV                  d      Z, ejV                  d      Z-dZ.de/de/fdZ0de1fd Z2d!e/de/fd"Z3 G d# d$e      Z4y)%at  BlueBubbles iMessage platform adapter.

Uses the local BlueBubbles macOS server for outbound REST sends and inbound
webhooks.  Supports text messaging, media attachments (images, voice, video,
documents), tapback reactions, typing indicators, and read receipts.

Architecture based on PR #5869 (benjaminsehl) with inbound attachment
downloading from PR #4588 (YuhangLin).
    N)OrderedDict)datetime)AnyDictListOptional)quote)PlatformPlatformConfig)BasePlatformAdapterMessageEventMessageType
SendResultcache_image_from_bytescache_audio_from_bytescache_document_from_bytesstrip_markdown	127.0.0.1i   i!  z/bluebubbles-webhooki  z#(?<![\w@])@?hermes\s+agent\b[,:\-]?z(?<![\w@])@?hermes\b[,:\-]?lovelikedislikelaugh	emphasizequestion)i  i  i  i  i  i  )i  i  i  i  i  i  >   messagenew-messageupdated-messagez\+?\d{7,15}z[\w.+-]+@[\w-]+\.[\w.]+i  textreturnc                 ^    t         j                  d|       } t        j                  d|       } | S )z0Redact phone numbers and emails from log output.z
[REDACTED])	_PHONE_REsub	_EMAIL_REr   s    P/root/.hermes/venv/lib/python3.12/site-packages/gateway/platforms/bluebubbles.py_redactr'   P   s'    ==t,D==t,DK    c                  4    	 dd l } dd l}y# t        $ r Y yw xY w)Nr   FT)aiohttphttpxImportError)r*   r+   s     r&   check_bluebubbles_requirementsr-   [   s&       s    	rawc                     | xs dj                         }|syt        j                  d|t        j                        sd| }|j	                  d      S )N z
^https?://)flagshttp:///)striprematchIrstrip)r.   values     r&   _normalize_server_urlr:   d   sI    YBE88M55%!<<r(   c                   T    e Zd Zej                  ZdZeZdZ	de
f fdZdedefdZededeej$                     fd	       Zd
edefdZd
edefdZdedeeef   fdZdedeeef   deeef   fdZdddedefdZdEdZedefd       Zedefd       Zedefd       Zdede fdZ!defdZ"defdZ#dede$e   fdZ%dedede&fd Z'eefd!ed"e(dee   fd#       Z)	 	 dFd$ed!ed%e$e   d&e$eeef      de&f
d'Z*	 	 	 dGd$ed(ed)e$e   d*e$e   d+ede&fd,Z+	 	 	 dHd$ed-ed*e$e   d%e$e   d&e$eeef      de&f fd.Z,	 	 dFd$ed/ed*e$e   d%e$e   de&f
d0Z-	 	 dFd$ed1ed*e$e   d%e$e   de&f
d2Z.	 	 dFd$ed3ed*e$e   d%e$e   de&f
d4Z/	 	 	 dHd$ed(ed*e$e   d5e$e   d%e$e   de&fd6Z0	 	 	 dHd$ed7ed*e$e   d%e$e   d&e$eeef      de&fd8Z1dId$eddfd9Z2d$eddfd:Z3d$edefd;Z4d$edeeef   fd<Z5d!edefd=Z6d>ed?eeef   de$e   fd@Z7deeef   de$eeef      fdAZ8edBede$e   fdC       Z9dD Z: xZ;S )JBlueBubblesAdapterFTconfigc                    t         |   |t        j                         |j                  xs i }t        |j                  d      xs t        j                  dd            | _	        |j                  d      xs t        j                  dd      | _
        |j                  d      xs t        j                  dt              | _        t        |j                  d      xs# t        j                  d	t        t                          | _        |j                  d
      xs t        j                  dt"              | _        t        | j$                        j'                  d      sd| j$                   | _        t)        |j                  dd            | _        |j                  d      }|t        j                  d      }t        |      j-                         j/                         dv | _        | j3                  d|v r|d   nt        j                  d            | _        d | _        d | _        d | _        d| _        t?               | _         y )N
server_urlBLUEBUBBLES_SERVER_URLr0   passwordBLUEBUBBLES_PASSWORDwebhook_hostBLUEBUBBLES_WEBHOOK_HOSTwebhook_portBLUEBUBBLES_WEBHOOK_PORTwebhook_pathBLUEBUBBLES_WEBHOOK_PATHr3   send_read_receiptsTrequire_mentionBLUEBUBBLES_REQUIRE_MENTION>   1onyestruemention_patternsBLUEBUBBLES_MENTION_PATTERNSF)!super__init__r
   BLUEBUBBLESextrar:   getosgetenvr?   rA   DEFAULT_WEBHOOK_HOSTrC   intstrDEFAULT_WEBHOOK_PORTrE   DEFAULT_WEBHOOK_PATHrG   
startswithboolrI   r4   lowerrJ   _compile_mention_patterns_mention_patternsclient_runner_private_api_enabled_helper_connectedr   _guid_cache)selfr=   rU   _require_mention	__class__s       r&   rS   zBlueBubblesAdapter.__init__z   s   !5!56"/IIl#Nryy1I2'N
 		*-V;QSU1VIIn% Kyy35IJ 	  IIn% Pyy3S9M5NO

 IIn% Kyy35IJ 	 4$$%005"#D$5$5#6 7D"&uyy1Et'L"M 99%67#!yy)FG"#34::<BBDHbb!%!?!?!U* $%9:"

 4848!',2=-r(   pathr    c                 f    d|v rdnd}| j                    | | dt        | j                  d       S )N?&z	password=r0   safe)r?   r	   rA   )rh   rk   seps      r&   _api_urlzBlueBubblesAdapter._api_url   s:    D[cc//"4&YuT]]QS7T6UVVr(   r.   c                    | t        t              }nt        | t              r| j	                         }	 |rt        j                  |      ng }t        |t               r|nC|j                         D cg c](  }|j                  d      D ]  }|j	                          * c}}}nt        | t               r| }n| g}g }|D ]R  }t        |      j	                         }|s	 |j                  t        j                  |t        j                               T |S # t        $ r d}Y w xY wc c}}w # t        j                  $ r!}t        j!                  d||       Y d}~d}~ww xY w)zCompile group-mention wake words from config/env.

        ``raw`` is a list (from config or env JSON), a string (raw env var:
        JSON list, or comma/newline-separated), or None (use Hermes defaults).
        N,z,[bluebubbles] Invalid mention pattern %r: %s)listDEFAULT_MENTION_PATTERNS
isinstancer[   r4   jsonloads	Exception
splitlinessplitappendr5   compile
IGNORECASEerrorloggerwarning)	r.   patternsr   loadedlinepartcompiledpatternexcs	            r&   ra   z,BlueBubblesAdapter._compile_mention_patterns   sJ    ;45HS!99;D-1D)r ",FD!9v OO-@ JJsO@  

@@H
 T"HuH') 	ZGw<%%'DZ

4 ?@	Z +  @" 88 ZMtUXYYZs/   D 2-D&3D,D#"D#,E ?EE r   c                 ^    r| j                   syt        fd| j                   D              S )NFc              3   @   K   | ]  }|j                          y wN)search).0r   r   s     r&   	<genexpr>zGBlueBubblesAdapter._message_matches_mention_patterns.<locals>.<genexpr>   s     NG7>>$'Ns   )rb   any)rh   r   s    `r&   !_message_matches_mention_patternsz4BlueBubblesAdapter._message_matches_mention_patterns   s(    411Nt7M7MNNNr(   c                     |s|S | j                   D ][  }|j                  |j                               }|s%|j                         |j                         d j                  d      }|xs |c S  |S )zStrip a leading BlueBubbles wake word before dispatch.

        Custom mention patterns are regular expressions, so stripping only a
        leading match avoids deleting ordinary words later in the prompt.
        Nz ,:-)rb   r6   lstripend)rh   r   r   r6   cleaneds        r&   _clean_mention_textz&BlueBubblesAdapter._clean_mention_text   sl     K-- 	'GMM$++-0E++-		5<<VD$&		'
 r(   c                    K   | j                   J | j                   j                  | j                  |             d {   }|j                          |j	                         S 7 $wr   )rc   rV   rr   raise_for_statusrx   )rh   rk   ress      r&   _api_getzBlueBubblesAdapter._api_get   sQ     {{&&&KKOODMM$$788xxz 9s   <A%A#%A%payloadc                    K   | j                   J | j                   j                  | j                  |      |       d {   }|j                          |j	                         S 7 $w)N)rx   )rc   postrr   r   rx   )rh   rk   r   r   s       r&   	_api_postzBlueBubblesAdapter._api_post   sY     {{&&&KK$$T]]4%8w$GGxxz Hs   >A' A%%A')is_reconnectr   c                  K   | j                   r| j                  st        j                  d       yddlm ddlm} t        j                  d |             | _
        	 | j                  d       d {    | j                  d	       d {   }|xs i j                  d
i       }t        |j                  d            | _        t        |j                  d            | _        t        j!                  d| j                   | j                  | j                         j'                  t(              }|j*                  j-                  dfd       |j*                  j/                  | j0                  | j2                         j5                  |d       | _        | j6                  j9                          d {    j;                  | j6                  | j<                  | j>                        }|jA                          d {    | jC                          t        j!                  d| j<                  | j>                  | j0                         | jE                          d {    y7 7 # t"        $ ra}t        j                  d| j                   |       | j                  r*| j                  j%                          d {  7   d | _
        Y d }~yd }~ww xY w7  7 7 }w)NzJ[bluebubbles] BLUEBUBBLES_SERVER_URL and BLUEBUBBLES_PASSWORD are requiredFr   web)platform_httpx_limitsg      >@)timeoutlimitsz/api/v1/pingz/api/v1/server/infodataprivate_apihelper_connectedz9[bluebubbles] connected to %s (private_api=%s, helper=%s)z+[bluebubbles] cannot reach server at %s: %s)client_max_sizez/healthc                 (    j                  d      S )Nokr%   )Response)_r   s    r&   <lambda>z,BlueBubblesAdapter.connect.<locals>.<lambda>  s    $0G r(   )
access_logz1[bluebubbles] webhook listening on http://%s:%s%sT)#r?   rA   r   r   r*   r   %gateway.platforms._http_client_limitsr   r+   AsyncClientrc   r   rV   r_   re   rf   inforz   acloseApplication_WEBHOOK_MAX_BODY_BYTESrouteradd_getadd_postrG   _handle_webhook	AppRunnerrd   setupTCPSiterC   rE   start_mark_connected_register_webhook)	rh   r   r   r   server_datar   appsiter   s	           @r&   connectzBlueBubblesAdapter.connect   s;    dmmLL\  	P''=R=TU	--///'<==D:2**626K(,[__]-K(LD%%)+//:L*M%ND"KKK))&&	& oo.EoF

9&GH

D--t/C/CD }}ST}:ll  """{{4<<):):D<M<MNjjl?		
 $$&&&] 0=  	LL=tPS {{kk((***"	( 	# 	's   AK I) 4I#5I) I&BI) BK6K7AK?K AKKK#I) &I) )	K2A
K<J?=K	KKKKKNc                 <  K   | j                          d {    | j                  r)| j                  j                          d {    d | _        | j                  r)| j                  j	                          d {    d | _        | j                          y 7 7 S7  wr   )_unregister_webhookrc   r   rd   cleanup_mark_disconnected)rh   s    r&   
disconnectzBlueBubblesAdapter.disconnect+  s}     &&(((;;++$$&&&DK<<,,&&(((DL! 	) ' )s3   BB-BB4B9B:BBBc                 b    | j                   }|dv rd}d| d| j                   | j                   S )z>Compute the external webhook URL for BlueBubbles registration.>   ::0.0.0.0	localhostr   r   r2   :)rC   rE   rG   )rh   hosts     r&   _webhook_urlzBlueBubblesAdapter._webhook_url7  sB       >>Da 1 1243D3D2EFFr(   c                 n    | j                   }| j                  r| dt        | j                  d       S |S )a  Webhook URL registered with BlueBubbles, including the password as
        a query param so inbound webhook POSTs carry credentials.

        BlueBubbles posts events to the exact URL registered via
        ``/api/v1/webhook``. Its webhook registration API does not support
        custom headers, so embedding the password in the URL is the only
        way to authenticate inbound webhooks without disabling auth.
        z
?password=r0   ro   )r   rA   r	   rh   bases     r&   _webhook_register_urlz(BlueBubblesAdapter._webhook_register_url?  s8       ==V:eDMM&C%DEEr(   c                 @    | j                   }| j                  r| dS |S )z'Webhook registration URL safe for logs.z?password=***)r   rA   r   s     r&   _webhook_register_url_for_logz0BlueBubblesAdapter._webhook_register_url_for_logN  s(       ==V=))r(   urlc                    K   	 | j                  d       d{   }|j                  d      }t        |t              r%|D cg c]  }|j                  d      |k(  s| c}S 	 g S 7 Mc c}w # t        $ r Y g S w xY ww)z1Return list of BB webhook entries matching *url*./api/v1/webhookNr   r   )r   rV   rw   ru   rz   )rh   r   r   r   whs        r&   _find_registered_webhooksz,BlueBubblesAdapter._find_registered_webhooksV  s     	&788C776?D$%%)BrRVVE]c-ABB & 	 9 C 			sV   A=A- A&)A- A(A( A- "A=$A=&A- (A- -	A:6A=9A::A=c                 (  K   | j                   sy| j                  }| j                  |       d{   }|r!t        j	                  d| j
                         y|ddgd}	 | j                  d|       d{   }|j                  d	d
      }d|cxk  rdk  r$n n!t        j	                  d| j
                         yt        j                  d||j                  d             y7 7 n# t        $ r }t        j                  d|       Y d}~yd}~ww xY ww)a  Register this webhook URL with the BlueBubbles server.

        BlueBubbles requires webhooks to be registered via API before
        it will send events.  Checks for an existing registration first
        to avoid duplicates (e.g. after a crash without clean shutdown).
        FNz,[bluebubbles] webhook already registered: %sTr   r   )r   eventsr   statusr      i,  z0[bluebubbles] webhook registered with server: %sz9[bluebubbles] webhook registration returned status %s: %sr   z8[bluebubbles] failed to register webhook with server: %s)
rc   r   r   r   r   r   r   rV   r   rz   )rh   webhook_urlexistingr   r   r   r   s          r&   r   z$BlueBubblesAdapter._register_webhooka  s     {{00 77DDKK>22  $&78

	'8'BBCWWXq)Ff"s"F66 OGGI&
 9 E C  	NNJ 	sX   .DC".D C& 5C$6AC& :D;&C& !D$C& &	D/D
D
DDc                   K   | j                   sy| j                  }d}	 | j                  |       d{   D ]]  }|j                  d      }|s| j                   j	                  | j                  d|              d{   }|j                          d}_ |r t        j                  d| j                         |S 7 7 ># t        $ r!}t        j                  d|       Y d}~|S d}~ww xY ww)zUnregister this webhook URL from the BlueBubbles server.

        Removes *all* matching registrations to clean up any duplicates
        left by prior crashes.
        FNidz/api/v1/webhook/Tz&[bluebubbles] webhook unregistered: %sz=[bluebubbles] failed to unregister webhook (non-critical): %s)rc   r   r   rV   deleterr   r   r   r   r   rz   debug)rh   r   removedr   wh_idr   r   s          r&   r   z&BlueBubblesAdapter._unregister_webhook  s      {{00	 ::;GG #t $ 2 2(8&@A! C ((*"G# <66 % H  	LLO  	sV   C-C  B<C  0C  ?B> :C  :C-<C  >C   	C*	C%C-%C**C-targetc                   K   |xs dj                         }|syd|v r|S || j                  v r*| j                  j                  |       | j                  |   S 	 | j                  dddd       d{   }|j	                  dg       xs g D ]  }|j	                  d	      xs |j	                  d
      }|j	                  d      xs |j	                  d      }||k(  sQ|rd|| j                  |<   t        | j                        t        kD  r9| j                  j                  d       t        | j                        t        kD  r9|c S  	 y7 # t        $ r Y yw xY ww)a  Resolve an email/phone to a BlueBubbles chat GUID.

        If *target* already contains a semicolon (raw GUID format like
        ``iMessage;-;user@example.com``), it is returned as-is.  Otherwise
        the adapter queries the BlueBubbles chat list and matches strictly
        on ``chatIdentifier`` / ``identifier``.

        Participant membership is intentionally NOT used as a fallback:
        the same contact can appear in a 1:1 DM and in any number of group
        chats, so a participant match would let an outbound DM reply leak
        into a group thread (see #24157). When no exact chat identity
        matches, return ``None`` and let the caller create a fresh DM
        explicitly via ``_create_chat_for_handle``.
        r0   N;z/api/v1/chat/queryd   r   )limitoffsetr   guidchatGuidchatIdentifier
identifierF)last)	r4   rg   move_to_endr   rV   len_GUID_CACHE_SIZEpopitemrz   )rh   r   r   chatr   r   s         r&   _resolve_chat_guidz%BlueBubblesAdapter._resolve_chat_guid  s]     ,B%%'&=MT%%%((0##F++	 NN$+ G  FB/52  xx'?488J+?!XX&67Q488L;Q
'37((0!$"2"236FF ,,44%4@ "$"2"236FFK    		sV   AEE 1E
2A*E A%E E EE 	E
E 	EEEEaddressr   c                   K   |g|dt        j                         j                          d}	 | j                  d|       d{   }|j	                  d      xs i }|j	                  d      xs |j	                  d      xs d}t        d	t        |      |
      S 7 X# t        $ r }t        dt        |            cY d}~S d}~ww xY ww)z<Create a new chat by sending the first message to *address*.temp-)	addressesr   tempGuidz/api/v1/chat/newNr   r   messageGuidr   Tsuccess
message_idraw_responseFr   r   )r   utcnow	timestampr   rV   r   r[   rz   )rh   r   r   r   r   r   msg_idr   s           r&   _create_chat_for_handlez*BlueBubblesAdapter._create_chat_for_handle  s     
 " 1 ; ; =>?

	='97CCC776?(bDXXf%H-)@HDFds6{QTUU D  	=e3s8<<	=sG   +C
B BAB C
B 	C'C<C=C
CC
content
max_lengthc                     t        j                  | |      }|D cg c]  }t        j                  dd|       c}S c c}w )Nz\s*\(\d+/\d+\)$r0   )r   truncate_messager5   r#   )r  r  chunkscs       r&   r
  z#BlueBubblesAdapter.truncate_message  s8     %55gzJ;ABa)2q1BBBs   <chat_idreply_tometadatac                   K   | j                  |      }|st        dd      S t        j                  d|      D cg c]#  }|j	                         s|j	                         % }}g }|xs |gD ]X  }	t        |	      | j                  k  r|j                  |	       -|j                  | j                  |	| j                               Z t        d      }
|D ]3  }| j                  |       d {   }|sT| j                  r6d|v st        j                  d	|      r| j                  ||       d {   c S t        dd
|       c S |dt        j                         j!                          |d}|r'| j                  r| j"                  rd|d<   ||d<   d|d<   	 | j%                  d|       d {   }|j'                  d      xs i }|j'                  d      xs |j'                  d      xs d}t        dt)        |      |      }
6 |
S c c}w 7 &7 7 g# t*        $ r"}t        dt)        |            cY d }~c S d }~ww xY ww)NFzBlueBubbles send requires textr  z\n\s*\n)r  T)r   @z^\+\d+z'BlueBubbles chat not found for target: r   )r   r   r   zprivate-apimethodselectedMessageGuidr   	partIndexz/api/v1/message/textr   r   r   r   r   )format_messager   r5   r|   r4   r   MAX_MESSAGE_LENGTHr}   extendr
  r   re   r6   r  r   r  r  rf   r   rV   r[   rz   )rh   r  r  r  r  r   p
paragraphsr  parar   chunkr   r   r   r   r  r   s                     r&   sendzBlueBubblesAdapter.send  s9     ""7+e3STT *,*d)CQAqwwyaggiQ
Q)D6 	_D4yD333d#d33DTE\E\3]^		_
 $' 	AE0099D,,7Nbhhy'&B!%!=!=gu!MMM!!CG9M 
 !#HOO$5$?$?$A#BC 'G
 D55$:P:P$1!19-.'($A NN+A7KKwwv,"&)LTXXm-DL! S[s3	A< M R : N L  A!%s3x@@Asz   9I
HH#BI
/H0AI
1H2A*I
H2H3AHI
I
H	I%I:I;I
II
	file_pathfilenamecaptionis_audio_messagec                   K   | j                   st        dd      S t        j                  j	                  |      st        dd|       S | j                  |       d{   }|st        dd|       S |xs t        j                  j                  |      }	 t        |d      5 }d||d	fi}	||t        j                         j                  d
}
|rd|
d<   | j                   j                  | j                  d      |	|
d       d{   }|j                          |j                         }ddd       |r| j                  ||       d{    j!                  d      dk(  rF|j!                  d      xs i }t#        |t$              r|j!                  d      nd}t        d||      S t        d|j!                  dd            S 7 i7 # 1 sw Y   xY w7 # t&        $ r }t        dt)        |            cY d}~S d}~ww xY ww)z8Send a file attachment via BlueBubbles multipart upload.FzNot connectedr  zFile not found: NzChat not found: rb
attachmentzapplication/octet-stream)r   namer   rO   isAudioMessagez/api/v1/message/attachmentx   )filesr   r   r   r   r   r   Tr   r   zAttachment upload failed)rc   r   rW   rk   isfiler   basenameopenuuiduuid4hexr   rr   r   rx   r  rV   rw   dictrz   r[   )rh   r  r  r  r  r   r   fnamefr'  r   r   resultrdatar  es                   r&   _send_attachmentz#BlueBubblesAdapter._send_attachment6  s     {{e?CCww~~i(e5Ei[3QRR,,W55e5EgY3OPP7BGG,,Y7!	;i& $!%q2L'MN $! $

 0 0(
 $-3D)* KK,,MM">?	 -   $$&!$$ ii111zz(#s*

6*0b.8.E6*4! V&  jj,FG C 6$ $& 2  	;e3q6::	;s   AHG 9HG &A"GG	$G-G GAG *H+G HGGG 	H$G?9H:H?HH	image_urlc                    K   	 ddl m}  ||       d {   }| j                  |||       d {   S 7  7 # t        $ r t        |   ||||       d {  7  cY S w xY ww)Nr   )cache_image_from_urlr  )gateway.platforms.baser7  r4  rz   rR   
send_image)	rh   r  r5  r  r  r  r7  
local_pathrj   s	           r&   r:  zBlueBubblesAdapter.send_imagel  sm     	SC3I>>J..w
G.TTT ?T 	S+GYRRRR	SsI   A%; 7; 9; A%; ; A"AA"A%!A""A%
image_pathc                 F   K   | j                  |||       d {   S 7 wNr8  r4  )rh   r  r<  r  r  kwargss         r&   send_image_filez"BlueBubblesAdapter.send_image_file|  &      **7J*PPPP   !!
audio_pathc                 H   K   | j                  |||d       d {   S 7 w)NT)r  r   r?  )rh   r  rD  r  r  r@  s         r&   
send_voicezBlueBubblesAdapter.send_voice  s3      **Z4 + 
 
 	
 
   " "
video_pathc                 F   K   | j                  |||       d {   S 7 wr>  r?  )rh   r  rH  r  r  r@  s         r&   
send_videozBlueBubblesAdapter.send_video  rB  rC  	file_namec                 H   K   | j                  ||||       d {   S 7 w)N)r  r  r?  )rh   r  r  r  rK  r  r@  s          r&   send_documentz BlueBubblesAdapter.send_document  s3      **YG + 
 
 	
 
rG  animation_urlc                 H   K   | j                  |||||       d {   S 7 wr   )r:  )rh   r  rN  r  r  r  s         r&   send_animationz!BlueBubblesAdapter.send_animation  s.      __]GXx
 
 	
 
rG  c                 @  K   | j                   r| j                  r| j                  sy 	 | j                  |       d {   }|rFt	        |d      }| j                  j                  | j                  d| d      d       d {    y y 7 M7 # t        $ r Y y w xY wwNr0   ro   /api/v1/chat/z/typing   r   re   rf   rc   r   r	   r   rr   rz   )rh   r  r  r   encodeds        r&   send_typingzBlueBubblesAdapter.send_typing  s     ((0F0Fdkk	0099D2.kk&&MMM''"BCQ '     :  		K   &BB BAB BB 	BB B 	BBBBc                 @  K   | j                   r| j                  r| j                  sy 	 | j                  |       d {   }|rFt	        |d      }| j                  j                  | j                  d| d      d       d {    y y 7 M7 # t        $ r Y y w xY wwrR  )re   rf   rc   r   r	   r   rr   rz   rh   r  r   rW  s       r&   stop_typingzBlueBubblesAdapter.stop_typing  s     ((0F0Fdkk	0099D2.kk((MMM''"BCQ )     :  		rY  c                 B  K   | j                   r| j                  r| j                  sy	 | j                  |       d {   }|rFt	        |d      }| j                  j                  | j                  d| d      d       d {    y	 y7 N7 	# t        $ r Y yw xY ww)	NFr0   ro   rS  z/readrT  rU  TrV  r[  s       r&   	mark_readzBlueBubblesAdapter.mark_read  s     ((0F0Fdkk		0099D2.kk&&MMM'%"@A1 '       :  		sQ   &BB BAB BB 	BBB B 	BBBBc                    K   d|xs dv }||rdndd}	 | j                  |       d {   }|rt        |d      }| j                  d| d       d {   }|xs i j                  d	i       }|j                  d
      xs |j                  d      xs |}g }	|j                  dg       xs g D ]9  }
|
j                  d      xs dj	                         }|s)|	j                  |       ; ||d<   |	r|	|d<   |S 7 7 # t        $ r Y |S w xY ww)N;+;r0   groupdm)r$  typero   rS  z?with=participantsr   displayNamer   participantsr   r$  )r   r	   r   rV   r4   r}   rz   )rh   r  is_groupr   r   rW  r   r   display_namere  r  addrs               r&   get_chat_infoz BlueBubblesAdapter.get_chat_info  sB    W]+'GT 
	0099D2. MM#G9,>?  	rvr2HH]+ xx 01 
  "."5; 2AEE),299;D$++D12  ,V+7D( - :"  		sQ   DC> C:+C> C<BC> C> 8D:C> <C> >	DD
DDc                     t        |      S r   r   )rh   r  s     r&   r  z!BlueBubblesAdapter.format_message  s    g&&r(   att_guidatt_metac                   K   | j                   sy	 t        |d      }| j                   j                  | j                  d| d      dd       d{   }|j	                          |j
                  }|j                  d	      xs dj                         }|j                  d
d      }|j                  d      r(dddddddd}|j                  |d      }	t        ||	      S |j                  d      r(dddddddd}|j                  |d      }	t        ||	      S |xs$ dt        j                         j                  dd  }
t        ||
      S 7 # t        $ r*}t        j!                  dt#        |      |       Y d}~yd}~ww xY ww)zDownload an attachment from BlueBubbles and cache it locally.

        Returns the local file path on success, None on failure.
        Nr0   ro   z/api/v1/attachment/z	/download<   T)r   follow_redirectsmimeTypetransferNameimage/z.jpgz.pngz.gifz.webp)z
image/jpegz	image/pngz	image/gifz
image/webpz
image/heicz
image/heifz
image/tiffaudio/z.mp3z.oggz.wavz.m4a)z	audio/mp3z
audio/mpegz	audio/oggz	audio/wavzaudio/x-cafz	audio/mp4z	audio/aacfile_   z2[bluebubbles] failed to download attachment %s: %s)rc   r	   rV   rr   r   r  r`   r^   r   r   r+  r,  r-  r   rz   r   r   r'   )rh   rk  rl  rW  respr   mimetransfer_nameext_mapextr  r   s               r&   _download_attachmentz'BlueBubblesAdapter._download_attachment  s     {{1	H2.G 3G9IFG!% )  D
 !!#<<DLL,299;D$LL<Mx("(!'!'")"("("( kk$/-dC88x(!'"(!'!'#)!'!' kk$/-dC88 %F%

0@0@!0D/E(FH,T8<<OR  	NND!
 	sZ   FAE EBE  F!8E F3E FE 	F E>9F>FFc                 &   |j                  d      }t        |t              r|S t        |t              r|D ]  }t        |t              s|c S  t        |j                  d      t              r|j                  d      S t        |t              r|S d S )Nr   r   )rV   rw   r.  ru   )rh   r   r   items       r&   _extract_payload_recordz*BlueBubblesAdapter._extract_payload_recordT  s     {{6"dD!KdD!  dD)K  gkk),d3;;y))$Wd3w==r(   
candidatesc                  x    | D ]5  }t        |t              s|j                         s%|j                         c S  y r   )rw   r[   r4   )r  	candidates     r&   _valuezBlueBubblesAdapter._valueb  s6    # 	)I)S)ioo.? ((	) r(   c                   K   ddl m} |j                  j                  d      xsr |j                  j                  d      xsU |j                  j                  d      xs8 |j                  j                  d      xs |j                  j                  d      }|| j
                  k7  r|j                  dd	id
      S 	 |j                          d {   }|j                  dd      }	 t        j                  |      }| j!                  |j                  d      |j                  d            xs d}|r|t"        vr|j%                  d      S | j'                  |      xs i }t)        |j                  d      xs$ |j                  d      xs |j                  d            }|r|j%                  d      S |j                  d      }t+        |t,              r"|i t.        t0        v r|j%                  d      S | j!                  |j                  d      |j                  d      |j                  d             xs d}|j                  d!      xs g }g }g }t2        j4                  }|D ]  }|j                  dd      }|s| j7                  ||       d {   }|s6|j                  d"      xs dj9                         }|j;                  |       |j;                  |       |j=                  d#      rt2        j>                  }|j=                  d$      s$|j                  d%      xs djA                  d&      rt2        jB                  }|j=                  d'      rt2        jD                  }t2        jF                  } tI        |      d(kD  r9|D ch c]  }|xs djK                  d)      d    }}d*|v rt2        j>                  }|s|rd+}| j!                  |j                  d,      |j                  d,      |j                  d-      |j                  d-      |j                  d            }|sT|j                  d.      xs g }|r=t+        |d   tL              r*|d   j                  d      xs |d   j                  d,      }| j!                  |j                  d/      |j                  d0      |j                  d/      |j                  d0            }| j!                  t+        |j                  d1      tL              r!|j                  d1i       j                  d2      nd |j                  d3      |j                  d4      |j                  d2            xs |xs |}|s|s|r|}|r|s|r|s|j                  dd5id      S |xs |}t)        |j                  d6            xs d7|xs dv }|rU| jN                  rI| jQ                  |      s't        jS                  d8       |j%                  d      S | jU                  |      }| jW                  ||xs ||rd9nd:|||;      } tY        ||| || j!                  |j                  d      |j                  d<      |j                  d=            | j!                  |j                  d>      |j                  d?            ||@      }!t[        j\                  | j_                  |!            }"| j`                  jc                  |"       |"je                  | j`                  jf                         | jh                  r&|r$t[        j\                  | jk                  |             |j%                  d      S 7 z# t        $ rj ddlm}  ||      }|j                  d      xs) |j                  d      xs |j                  d      xs dgd   }	|	rt        j                  |	      ni }Y w xY w# t        $ r5}
t        j                  d|
       |j                  ddid      cY d }
~
S d }
~
ww xY w7 mc c}w w)ANr   r   rA   r   z
x-passwordzx-guidzx-bluebubbles-guidr   unauthorizedi  )r   zutf-8replace)errors)parse_qsr   r   r   r0   z%[bluebubbles] webhook parse error: %szinvalid payloadi  rc  eventr   r%   isFromMefromMe
is_from_meassociatedMessageTyper   bodyattachmentsrp  rr  rs  uticafzvideo/   r3   imagez(attachment)r   	chat_guidchatsr   r   handler   senderfromzmissing message fieldsisGroupr`  zW[bluebubbles] ignoring group message (require_mention=true, no mention pattern matched)ra  rb  )r  	chat_name	chat_typeuser_id	user_namechat_id_altr   r   threadOriginatorGuidassociatedMessageGuid)r   message_typesourceraw_messager   reply_to_message_id
media_urlsmedia_types)6r*   r   queryrV   headersrA   json_responsereaddecoderx   ry   rz   urllib.parser  r   r   r  _MESSAGE_EVENTSr   r~  r_   rw   rZ   _TAPBACK_ADDED_TAPBACK_REMOVEDr   TEXTr{  r`   r}   r^   PHOTOendswithVOICEVIDEODOCUMENTr   r|   r.  rJ   r   r   r   build_sourcer   asynciocreate_taskhandle_message_background_tasksaddadd_done_callbackdiscardrI   r^  )#rh   requestr   tokenr.   r  r   r  formpayload_strr   
event_typerecordr  
assoc_typer   r  r  r  msg_typeattrk  cachedrw  mmime_prefixesr  _chatschat_identifierr  session_chat_idrf  r  r  tasks#                                      r&   r   z"BlueBubblesAdapter._handle_webhooki  sc     MMj) 9}}  (9""<09 ""8,9 ""#78 	 DMM!$$g~%>s$KK	O&C::gi:8DI**T*  [[V!4gkk'6JKQr
*O;<<T<**--g6<"JJz" (zz(#(zz,'


 <<T<** ZZ 78
j#&: :
:
:
 ,
 <<T<** KK

6"FJJy$96::f;M  	 	 jj/52 "
!### 	4Cwwvr*H44XsCCF
+1r88:!!&)""4(??8,*00H__X.3775>3GR2Q2Q3  +00H__X.*00H*33H%	4* z?Q>IJag2__S1!4JMJ-'&,,
!D KKJJz"KK
#JJ{#KK$KK
	 ZZ(.BF*VAY5"1IMM&1NVAY]]:5N	++JJ'(JJ|$KK()KK%	
 KKfjj2D9 

8R(,,Y7

8$

6"

9% 	 	  	 _&$Oi?4$$g/G%HQT$UU#6

9-.N5Y_"3M,,99$?m |||..++D1D""#%/!)gt' # 
 !{{

6"

=)

4 
 !%

12

23! "#
" ""4#6#6u#=>""4(t55==> "" ?@|||&&E '  
I1~HHY' xx'xx	* t 6A$**[1b
I  	OLL@#F$$g/@%A#$NN	OV D$ Ks   B:`=_ ]
_ )] >F```C5``3N`
_ A/_ <_ ?_  _ 	`*_<6`7`<```)r    N)NN)NNF)NNNr   )<__name__
__module____qualname__r
   rT   platformSUPPORTS_MESSAGE_EDITINGMAX_TEXT_LENGTHr  splits_long_messagesr   rS   r[   rr   staticmethodr   r   r5   Patternra   r_   r   r   r   r   r   r   r   propertyr   r   r   ru   r   r   r   r   r   r   r  rZ   r
  r  r4  r:  rA  rF  rJ  rM  rP  rX  r\  r^  ri  r  r{  r~  r  r   __classcell__)rj   s   @r&   r<   r<   t   sh   ##H$(#@~ #@RWS WS W !s !tBJJ/? ! !FOc Od O
  3 4S> C $sCx. T#s(^  5: :T :d :x
" Gc G G s   s  	3 	4 	/ /b4 J(s (x} (T==%(=	=* 9H C# C3 CTRUY C C #'-133 3 3-	3
 4S>*3 
3z #'!%!&4;4; 4; 3-	4;
 #4; 4; 
4;t "&"&-1SS S #	S
 3-S 4S>*S 
S( "&"&QQ Q #	Q
 3-Q 
Q "&"&



 

 #	


 3-

 


  "&"&QQ Q #	Q
 3-Q 
Q "&#'"&

 
 #	

 C=
 3-
 

" "&"&-1



 

 #	


 3-

 4S>*

 


     "s t .3 4S> >'c 'c '::'+CH~:	#:@>CH~>	$sCx.	!> C HSM  o'r(   r<   )5__doc__r  rx   loggingrW   r5   r+  collectionsr   r   typingr   r   r   r   r  r	   r+   gateway.configr
   r   r9  r   r   r   r   r   r   r   gateway.platforms.helpersr   	getLoggerr  r   rY   r   r\   r]   r  rv   r  r  r  r~   r"   r$   r   r[   r'   r_   r-   r:   r<    r(   r&   <module>r     s+      	 	  #  , ,   3   5			8	$ #  $  -  +"  i
J
 i
J  @ BJJ~&	BJJ12	 # #  s s  d', d'r(   