+
    i4                     J   R t ^ RIt^ RIt^ RIHtHt ^ RIHtHtHtH	t	H
t
  ^ RItRtRR^
R^/RR^R^/R	R^(R^//tR
t]! 0 RkRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkR kR!kR"kR#kR$kR%kR&kR'kR(kR)kR*kR+kR,kR-kR.kR/kR0kR1kR2kR3kR4kR5kR6k4      tR7R8R90R:R8R90R80 R]mR90 R^mR;R<0R<R;0R=R>0R>R=0R?R@RA0RBRCRD0RERF0RFRE0/tRG RH ltR_RI RJ lltRK RL ltRM RN ltRO RP ltRQ RR ltRS RT ltR`RU RV lltRaRW RX lltR`RY RZ lltR[ R\ ltR#   ] d    Rt Li ; i)ba>  TikTok search via ScrapeCreators API for /last30days.

Uses ScrapeCreators REST API to search TikTok by keyword, extract engagement
metrics (views, likes, comments, shares), and fetch video transcripts.

Requires SCRAPECREATORS_API_KEY in config. 100 free credits, then PAYG.
API docs: https://scrapecreators.com/docs
N)datetimetimezone)AnyDictListOptionalSetz(https://api.scrapecreators.com/v1/tiktokquickresults_per_pagemax_captionsdefaultdeepi  theaantoforhowisinofonandwithfrombyatthisthatitmyyourimeweyouwhataredocanitsbeornotnosoifbutaboutalljustgethashavewaswillhipraphiphophopjs
javascriptts
typescriptai
artificialintelligencemlmachinelearningreactreactjsc                F    V ^8  d   QhR\         R\        \         ,          /#    textreturn)strr   )formats   "R/Users/bowang/.openclaw/workspace/skills/last30days-official/scripts/lib/tiktok.py__annotate__rR   :   s      C CH     c                P   \         P                  ! RRV P                  4       4      P                  4       pV Uu0 uF#  q"\        9  g   K  \        V4      ^8  g   K!  VkK%  	  pp\        V4      pV F,  pV\        9   g   K  VP                  \        V,          4       K.  	  V# u upi )zHLowercase, strip punctuation, remove stopwords, drop single-char tokens.z[^\w\s] )	resublowersplit	STOPWORDSlensetSYNONYMSupdate)rM   wordswtokensexpandedts   &     rQ   	_tokenizerd   :   s    FF:sDJJL1779EDA9"4aQ!aaFD6{H=OOHQK(  O Es   B#
B#B#c                ^    V ^8  d   QhR\         R\         R\        \         ,          R\        /# )rL   queryrM   hashtagsrN   )rO   r   float)rP   s   "rQ   rR   rR   E   s,     % %c % %S	 %U %rS   c                   \        V 4      pTpV'       d   V RRP                  V4       2p\        V4      pV'       dD   V F=  pVP                  4       pV F$  pW9   g   K  W8w  g   K  VP                  V4       K&  	  K?  	  V'       g   R# \	        W5,          4      p	V	\	        V4      ,          p
\        R\        RV
4      4      # )zCompute relevance as ratio of query tokens found in text + hashtags.

Uses ratio overlap (intersection / query_length). Hashtags provide
a TikTok-specific relevance boost. Floors at 0.1.
rU   g      ?g?g      ?)rd   joinrX   addr[   maxmin)rf   rM   rg   q_tokenscombinedt_tokenstag	tag_lowerqtoverlapratios   &&&        rQ   _compute_relevancerv   E   s     H HV1SXXh/01"H C		I?rLL$   (%&Gc(m#EsCUO$$rS   c                0    V ^8  d   QhR\         R\         /# )rL   topicrN   rO   )rP   s   "rQ   rR   rR   c   s                rS   c                   V P                  4       P                  4       p. ROpV F?  pVP                  VR,           4      '       g   K#  V\        V4      R P                  4       pKA  	  0 RmpVP	                  4       pV Uu. uF  qfV9  g   K  VNK  	  ppV'       d   RP                  V4      MTpVP                  R4      # u upi )zExtract core subject from verbose query for TikTok search.

Strips meta/research words to keep only the core product/concept name.
rU   Nz?!.)zwhat are the bestzwhat is the bestzwhat are the latestzwhat are people saying aboutzwhat do people think aboutzhow do i usez
how to usezhow tozwhat arezwhat isztips forzbest practices for>   newtopbestgoodnewsgreatviraladvicekillerlatestpromptr^   awesomehottestmethodspopularpromptsupdatesfeaturestrending	practices	prompting
approaches
strategiesrecommendations)rX   strip
startswithr[   rY   rj   rstrip)	rx   rM   prefixespnoiser_   r`   filteredresults	   &        rQ   _extract_core_subjectr   c   s    
 ;;= DH ??1s7##A=&&(D 
E JJLE 35aUN5H3#+SXXhF== 4s    CCc                $    V ^8  d   QhR\         /# )rL   msgry   )rP   s   "rQ   rR   rR      s      c rS   c                    \         P                  P                  4       '       dD   \         P                  P                  RV  R24       \         P                  P	                  4        R# R# )zGLog to stderr (only in interactive terminals; spinner handles non-TTY).z	[TikTok] 
N)sysstderrisattywriteflush)r   s   &rQ   _logr      sE    
zz

9SE,-

 rS   c                R    V ^8  d   QhR\         R\        \         \         3,          /# )rL   tokenrN   )rO   r   )rP   s   "rQ   rR   rR      s"      s tCH~ rS   c                    RV RR/# )z%Build ScrapeCreators request headers.z	x-api-keyzContent-Typezapplication/json )r   s   &rQ   _sc_headersr      s     	U* rS   c                h    V ^8  d   QhR\         \        \        3,          R\        \        ,          /# )rL   itemrN   )r   rO   r   r   )rP   s   "rQ   rR   rR      s&      d38n # rS   c                    V P                  R4      pV'       dB    \        P                  ! \        V4      \        P
                  R7      pVP                  R4      # R#   \        \        \        3 d     R# i ; i)zaParse date from ScrapeCreators TikTok item to YYYY-MM-DD.

Handles create_time (unix timestamp).
create_time)tzz%Y-%m-%dN)
r5   r   fromtimestampintr   utcstrftime
ValueError	TypeErrorOSError)r   r@   dts   &  rQ   _parse_dater      sf    
 
-	 B		''BHLLAB;;z**  Iw/ 		s   ?A A76A7c                0    V ^8  d   QhR\         R\         /# rK   ry   )rP   s   "rQ   rR   rR      s        rS   c                H   V '       g   R# V P                  R4      p. pV Fo  pVP                  4       pV'       g   K  VP                  R4      '       d   K6  \        P                  ! RV4      '       d   KU  RV9   d   K^  VP                  V4       Kq  	  RP                  V4      # )z9Strip WebVTT timestamps and headers from transcript text. r   WEBVTTz^\d{2}:\d{2}z-->rU   )rY   r   r   rV   matchappendrj   )rM   linescleanedlines   &   rQ   _clean_webvttr      s    JJtEGzz|??8$$88OT**D=t  88GrS   c                    V ^8  d   QhR\         R\         R\         R\         R\         R\        \         \        3,          /# rL   rx   	from_dateto_datedepthr   rN   rO   r   r   )rP   s   "rQ   rR   rR      sT     s sss s 	s
 s 
#s(^srS   c                
	   V'       g   R. RR/# \         '       g   R. RR/# \        P                  V\        R,          4      p\        V 4      p\	        RV RV RVR	,           R
24        \         P                  ! \
         R2RVRR/\        V4      ^R7      pVP                  4        VP                  4       pTP                  R4      ;'       g    TP                  R4      ;'       g    . p
. pT
 F>  p\        T\        4      '       g   K  TP                  RT4      pTP                  T4       K@  	  TRTR	,           p. pT EF.  p\        TP                  RR4      4      pTP                  RR4      pTP                  R4      ;'       g    / pTP                  R4      ;'       g    ^ pTP                  R4      ;'       g    ^ pTP                  R4      ;'       g    ^ pTP                  R4      ;'       g    ^ pTP                  R4      ;'       g    / pTP                  RR4      pTP                  R R4      pTP                  R!4      ;'       g    . pT Uu. uFF  p\        T\        4      '       g   K  TP                  R"4      '       g   K4  TP                  R"R4      NKH  	  ppTP                  R#4      ;'       g    / P                  R$4      p\!        T4      p\#        TTT4      pT'       d   TP%                  R%4      ^ ,          MRp T '       g   T'       d   T'       d	   R&T R'T 2p TP                  R(TR)TR*T R+TR,TR-R.TR/TR0TR1T/R2TR$TRTR3T'       d   R4TR5,           2MR4T 2R6R/4       EK1  	  T U!u. uF2  p!T!R,,          '       g   K  TT!R,,          u;8:  d
   T8:  g   K,  M K0  T!NK4  	  p"p!\'        T4      \'        T"4      ,
          p#T"'       d   T"pT#'       d   \	        R7T# R824       M\	        R9\'        T4       24       TP)                  R: R;R<7       \	        R=\'        T4       R>24       RT/#   \         d7   p	\	        RT	 24       R. R\        T	4      P                   RT	 2/u Rp	?	# Rp	?	ii ; iu upi u up!i )?a  Search TikTok via ScrapeCreators API.

Args:
    topic: Search topic
    from_date: Start date (YYYY-MM-DD)
    to_date: End date (YYYY-MM-DD)
    depth: 'quick', 'default', or 'deep'
    token: ScrapeCreators API key

Returns:
    Dict with 'items' list and optional 'error'.
itemserrorz$No SCRAPECREATORS_API_KEY configuredzrequests library not installedr   zSearching TikTok for 'z	' (depth=z, count=r
   )z/search/keywordrf   sort_by	relevanceparamsheaderstimeoutzScrapeCreators error: : Nsearch_item_listdata
aweme_infoaweme_idr   desc
statistics
play_count
digg_countcomment_countshare_countauthor	unique_id	share_url
text_extrahashtag_namevideoduration?zhttps://www.tiktok.com/@z/video/video_idrM   urlauthor_namedate
engagementviewslikescommentssharesrg   why_relevantzTikTok: :N<   Ncaption_snippetz	Filtered z videos outside date rangez)No videos within date range, keeping all c                 "    V R ,          R,          # )r   r   r   )xs   &rQ   <lambda>search_tiktok.<locals>.<lambda>)  s    Q|_W5rS   T)keyreversezFound z TikTok videos)	_requestsDEPTH_CONFIGr5   r   r   SCRAPECREATORS_BASEr   raise_for_statusjson	Exceptiontype__name__
isinstancedictr   rO   r   rv   rY   r[   sort)$rx   r   r   r   r   config
core_topicrespr   eraw_entries	raw_itemsentryinfor   rawr   rM   statsr   r   r   r   r   r   r   r   rc   hashtag_namesr   date_strr   r   r"   in_rangeout_of_ranges$   &&&&&                               rQ   search_tiktokr     sW   & W&LMM9W&FGGe\)%<=F&u-J!*YugXfM_F`Eaab	cdB}}"#?3ZK@&	
 	yy{ ((-.HH$((62BHHbKIeT""99\51DT"  56"456I Eswwz2./wwvr"%++YY|,11
YY|,11
		/277aii.33!"((bjjb1GGK,	WW\*00b
<F KJq&q$/ 345EE.4I 3~r2J KGGG$**//
;s# 'z4G	 *3iooc"1%{x,[M
KCD3;HM+	 dhtCyk2(:,@Wr!
 	5 \ !T5aAfII)qy2SG2S2S5HTu:H-L9\N*DEF8UEF 
JJ5tJD6#e*^	,-UY  B%aS)*Wa)9)9(:"QC&@AAB:KD UsI   *AP7 Q;5Q;Q;9R R 'R +R 7Q8+Q3-Q83Q8c          
          V ^8  d   QhR\         \        \        \        3,          ,          R\        R\        R\        \        \        3,          /# )rL   video_itemsr   r   rN   )r   r   rO   r   )rP   s   "rQ   rR   rR   /  sK     F Fd38n%FF F 
#s(^	FrS   c                   \         P                  V\         R,          4      pVR,          pV '       d   V'       d   \        '       g   / # V RV p\        R\	        V4       R24       / pV Fo  pVR,          pVP                  RR4      p	V	'       g   K(  V	P                  4       p
\	        V
4      \        8  d    R	P                  V
R\         4      R
,           p	WV&   Kq  	  V EF!  pVR,          pVP                  RR4      pV'       g   K)   \        P                  ! \         R2RV/\        V4      ^R7      pVP                  ^8X  d   VP                  4       pVP                  R4      pV'       d   \        V\        4      '       d   R	P                  R V 4       4      p\        V4      pV'       dK   VP                  4       p
\	        V
4      \        8  d    R	P                  V
R\         4      R
,           pWV&   EK  EK  EK!  EK$  	  \!        R VP#                  4        4       4      p\        RV R\	        V4       R24       V#   \         d   p\        RT RT 24        Rp?EK  Rp?ii ; i)a  Fetch transcripts for top N TikTok videos via ScrapeCreators.

Strategy:
1. Use the 'text' field (video description) as baseline caption
2. For top N, call /video/transcript for spoken-word captions

Args:
    video_items: Items from search_tiktok()
    token: ScrapeCreators API key
    depth: Depth level for caption limit

Returns:
    Dict mapping video_id -> caption text (truncated to 500 words)
r   r   NzEnriching captions for z videosr   rM   r   rU   z...r   z/video/transcriptr   
transcriptc              3   8   "   T F  p\        V4      x  K  	  R # 5iNry   ).0ss   & rQ   	<genexpr>!fetch_captions.<locals>.<genexpr>i  s     -Ijc!ffjs   zTranscript fetch failed for r   c              3   8   "   T F  q'       g   K  ^x  K  	  R# 5i)   Nr   )r  vs   & rQ   r  r  s  s     0*Aaaa*s   	
zGot captions for /)r   r5   r   r   r[   rY   CAPTION_MAX_WORDSrj   r   r   status_coder   r   listr   r   sumvalues)r  r   r   r  r   	top_itemscaptionsr   vidrM   r_   r   r  r   r  r  gots   &&&              rQ   fetch_captionsr)  /  s+   & e\)%<=F.)Le99	M\*I"3y>"2'	:;H :xx#4JJLE5z--xx&8'8 9:UB SM  :hhub!	<==&''89s|#E*	D 3&yy{!XXl3
!*d33%(XX-Ij-I%I
!.z!:J! * 0 0 2u:(99),%8J9J2K)Lu)TJ(2	 "	  ' 6 0*0
0CSE3y>"2'	:;O  	</uBqc:;;	<s    	A%I/BII*I%%I*c                    V ^8  d   QhR\         R\         R\         R\         R\         R\        \         \        3,          /# r   r   )rP   s   "rQ   rR   rR   x  sT     $A $A$A$A $A 	$A
 $A 
#s(^$ArS   c                    \        WW#V4      pVP                  R. 4      pV'       g   V# \        WdV4      pV F+  pVR,          p	VP                  V	4      p
V
'       g   K'  WR&   K-  	  RVRVP                  R4      /# )aI  Full TikTok search: find videos, then fetch captions for top results.

Args:
    topic: Search topic
    from_date: Start date (YYYY-MM-DD)
    to_date: End date (YYYY-MM-DD)
    depth: 'quick', 'default', or 'deep'
    token: ScrapeCreators API key

Returns:
    Dict with 'items' list. Each item has a 'caption_snippet' field.
r   r   r   r   )r  r5   r)  )rx   r   r   r   r   search_resultr   r&  r   r'  captions   &&&&&      rQ   search_and_enrichr.  x  s    ( "%GEJMgr*E eE2H :,,s#7&-"#	  UG]%6%6w%?@@rS   c                    V ^8  d   QhR\         \        \        3,          R\        \         \        \        3,          ,          /# )rL   responserN   )r   rO   r   r   )rP   s   "rQ   rR   rR     s/     % %DcN %tDcN7K %rS   c                &    V P                  R. 4      # )zmParse TikTok search response to normalized format.

Returns:
    List of item dicts ready for normalization.
r   )r5   )r0  s   &rQ   parse_tiktok_responser2    s     <<$$rS   >   r:   r=   r<   >   r:   r=   r;   r  )r   N)r   )__doc__rV   r   r   r   typingr   r   r   r   r   requestsr   ImportErrorr   r   r   	frozensetrZ   r]   rd   rv   r   r   r   r   r   r  r)  r.  r2  r   rS   rQ   <module>r8     s   
 
 ' 1 1  A  "B:"B:
"B:    	!#(*.046:<@	!%'-/57;=A     # %+ -2 48 :? 
	 	 	 	 #	 %)	 +/	 16	 8?	
 

 
 
  
 "(
 */
 17 	 
E8	E8	##<.4&<.4&<
(9j
!i[y %<  F (slFR$AN%]  Is   D 	D"!D"