+
    iGJ                     h   R t ^ RIt^ RIt^ RIHt ^ RIHtHt ^ RIHtH	t	H
t
HtHt  ^ RIt^RIHt RtRR^R	^R
^RR/RR^R	^R
^RR/RR^R	^R
^R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R0kR1kR2kR3kR4kR5kR6kR7kR8kR9k4      tR: R; ltR< R= ltR> R? ltR@ RA lt]! 0 RXm4      tRYRB RC lltRD RE ltRZRF RG lltR[RH RI lltR[RJ RK lltRL RM lt RN RO lt!R\RP RQ llt"R]RR RS llt#R\RT RU llt$RV RW lt%R#   ] d    Rt ELi ; i)^a  Reddit search via ScrapeCreators API for /last30days.

Uses ScrapeCreators REST API to search Reddit globally, discover relevant
subreddits, run targeted subreddit searches, and fetch comment trees.

Replaces openai_reddit.py as the primary Reddit search backend.
Falls back to openai_reddit.py if SCRAPECREATORS_API_KEY is missing but
OPENAI_API_KEY is present.

Requires SCRAPECREATORS_API_KEY in config (same key as TikTok + Instagram).
API docs: https://scrapecreators.com/docs
N)Counter)datetimetimezone)AnyDictListOptionalSet)httpz(https://api.scrapecreators.com/v1/redditquickglobal_searchessubreddit_searchescomment_enrichments	timeframeweekdefaultmonthdeepbesttopgoodgreatawesomekillerlatestnewnewsupdateupdatestrendinghottestpopular	practicesfeaturestipsrecommendationsadvicepromptprompts	promptingmethods
strategies
approacheshowtotheaanforwithofinonisarewhatwhichguidetutorialusingc                $    V ^8  d   QhR\         /# )   msgstr)formats   "R/Users/bowang/.openclaw/workspace/skills/last30days-official/scripts/lib/reddit.py__annotate__rE   B   s      c     c                    \         P                  P                  RV  R24       \         P                  P                  4        R# )zLog to stderr.z	[Reddit] 
N)sysstderrwriteflush)r@   s   &rD   _logrM   B   s-    JJyR()JJrF   c                R    V ^8  d   QhR\         R\        \         \         3,          /# )r?   tokenreturn)rB   r   )rC   s   "rD   rE   rE   H   s"      s tCH~ rF   c                    RV RR/# )z%Build ScrapeCreators request headers.z	x-api-keyzContent-Typezapplication/json )rO   s   &rD   _sc_headersrS   H   s     	U* rF   c                0    V ^8  d   QhR\         R\         /# )r?   topicrP   rA   )rC   s   "rD   rE   rE   P   s            rF   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  	  VP	                  4       pV Uu. uF  qU\
        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 )zqExtract core subject from verbose query.

Strips meta/research words to keep only the core product/concept name.
 N?!.)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)lowerstrip
startswithlensplitNOISE_WORDSjoinrstrip)rU   textprefixespwordswfilteredresults   &       rD   _extract_core_subjectrh   P   s    
 ;;= DH ??1s7##A=&&(D  JJLE 95a[$85H9#+SXXhF== :s   <CCc                R    V ^8  d   QhR\         R\         R\        \         ,          /# )r?   rU   depthrP   )rB   r   )rC   s   "rD   rE   rE   i   s%       S T#Y rF   c                p   \        V 4      pV.pV P                  4       P                  R4      pVP                  4       VP                  4       8w  d0   \	        VP                  4       4      ^8:  d   VP                  V4       VR9   d   VP                  V R24       VR8X  d   VP                  V R24       V# )aA  Generate multiple Reddit search queries from a topic.

Uses local logic (no LLM call needed):
1. Extract core subject (strip noise words)
2. Include original topic if different from core
3. For default/deep: add casual/review variant
4. For deep: add problem/issues variant

Returns 1-4 query strings depending on depth.
rX   r   z worth it OR thoughts OR reviewz$ issues OR problems OR bug OR broken)r   r   )rh   rZ   r`   rY   r\   r]   append)rU   rj   corequeriesoriginal_cleans   &&   rD   expand_reddit_queriesrp   i   s     !'DfG [[]))%0Nzz|~++--#n6J6J6L2MQR2R~&##$>?@$CDENrF   c          	          V ^8  d   QhR\         \        \        \        3,          ,          R\        R\        R\         \        ,          /# )r?   resultsrU   max_subsrP   )r   r   rB   r   int)rC   s   "rD   rE   rE      sA     .< .<$sCx.!.<.< .< 
#Y	.<rF   c                  a V'       d   \        V4      MRpV'       d(   \        VP                  4       P                  4       4      M	\        4       p\	        4       pV  F  pVP                  RR4      pV'       g   K  RpVP                  4       oV'       dJ   \        ;QJ d    V3R lV 4       F  '       g   K   RM	  RM! V3R lV 4       4      '       d
   VR,          pS\        9   d
   VR,          pVP                  R	4      ;'       g    VP                  R
^ 4      p	V	'       d   V	^d8  d
   VR,          pWW;;,          V,          uu&   K  	  VP                  V4       UU
u. uF  w  rzVNK	  	  up
p# u up
pi )a  Extract top subreddits from global search results with relevance weighting.

Uses frequency + topic-word matching + utility-sub penalties + engagement
bonus to find discussion subs rather than utility/meta subs.

Args:
    results: List of post dicts from global search
    topic: Original search topic (for relevance matching)
    max_subs: Maximum subreddits to return

Returns:
    Top subreddit names sorted by weighted score
 	subredditg      ?c              3   R   <"   T F  p\        V4      ^8  g   K  VS9   x  K  	  R# 5i)r?   N)r\   ).0re   	sub_lowers   & rD   	<genexpr>&discover_subreddits.<locals>.<genexpr>   s!     OjCFQJna9njs   ''TFg       @g333333?upsscoreg      ?)	rh   setrY   r]   r   getanyUTILITY_SUBSmost_common)rr   rU   rs   rm   
core_wordsscorespostsubbaser}   _rz   s   &&&        @rD   discover_subredditsr      s   $ ,1 'bD.2TZZ\'')*JYFhh{B'  IIK	##OjO###OjOOOCKD $CKD hhuo55'1!539CKDt- 0 %00:;:FCC:;;;s   !E2c                :    V ^8  d   QhR\         \        ,          /# )r?   rP   )r   rB   )rC   s   "rD   rE   rE      s       rF   c                    V '       g   R#  \         P                  ! \        V 4      \        P                  R7      pVP                  R4      #   \        \        \        3 d     R# i ; i)z%Convert Unix timestamp to YYYY-MM-DD.N)tzz%Y-%m-%d)	r   fromtimestampfloatr   utcstrftime
ValueError	TypeErrorOSError)created_utcdts   & rD   _parse_dater      sP    ##E+$68<<H{{:&&	7+ s   ?A A&%A&c          
          V ^8  d   QhR\         \        \        3,          R\        R\        R\         \        \        3,          /# )r?   r   idxsource_labelrP   )r   rB   r   rt   )rC   s   "rD   rE   rE      s=      $sCx. s # UYZ]_bZbUc rF   c                   V P                  RR4      pV'       d   RV 2MV P                  RR4      pV'       d
   RV9  d   RpRRV 2RV P                  RR4      R	\        V P                  R	R4      4      P                  4       RTR
\        V P                  R
R4      4      P                  4       R\        V P                  R4      4      RRV P                  R4      ;'       g    V P                  R^ 4      RV P                  R^ 4      RV P                  R4      /RRRRV R2R\        V P                  RR4      4      R,          /
# )z>Normalize a ScrapeCreators Reddit post to our internal format.	permalinkrv   zhttps://www.reddit.comurlz
reddit.comidR	reddit_idtitlerw   dater   
engagementr~   r}   num_commentsupvote_ratio	relevancegffffff?why_relevantzReddit z searchselftext:Ni  N)r   rB   rZ   r   )r   r   r   r   r   s   &&&  rD   _normalize_postr      s0   b)I2;"9+
.%QSATC |3& 	#iTXXdB'TXXgr*+113sS+r2399;DHH]34TXXe_<<!(<DHH^Q7DHH^4

 	S',w7CR01$7 rF   c                    V ^8  d   QhR\         R\         R\         R\         R\        \        \         \        3,          ,          /# )r?   queryrO   sortr   rP   rB   r   r   r   )rC   s   "rD   rE   rE      sG     , ,,, , 	,
 
$sCx.,rF   c           	        \         '       g   \        R4        ^ RIHp V! RV RVRV/4      p\         RV 2p\        V4      p\        P                  VR&   \        P                  ! Wg^^R7      pVP                  R	VP                  R
. 4      4      #  \         P                  ! \         R2RV RVRV/\        V4      ^R7      p
V
P                  4        V
P                  4       pVP                  R	VP                  R
. 4      4      #   \         d   p	\        RT	 24       . u Rp	?	# Rp	?	ii ; i  \         d   p	\        RT	 24       . u Rp	?	# Rp	?	ii ; i)a  Search across all of Reddit via ScrapeCreators global search.

Args:
    query: Search query
    token: ScrapeCreators API key
    sort: Sort order (relevance, hot, top, new)
    timeframe: Time filter (hour, day, week, month, year, all)

Returns:
    List of post dicts
z6requests library not installed, falling back to urllib	urlencoder   r   r   z/search?
User-AgentheaderstimeoutretriespostsdatazGlobal search error (urllib): Nz/searchparamsr   r   zGlobal search error: )	_requestsrM   urllib.parser   SCRAPECREATORS_BASErS   r
   
USER_AGENTr   	Exceptionraise_for_statusjson)r   rO   r   r   r   r   r   r   r   eresps   &&&&       rD   _global_searchr      sC   " 9EF
	.k9UVF()&:C!%(G$(OOGL!88C"aHD88GTXXfb%9::
}}"#7+UFD+yI&	
 	yy{xx&"!566  	1!56I	  $QC()	s=   A7D A1D, D)D$D)$D),E7EEEc                    V ^8  d   QhR\         R\         R\         R\         R\         R\        \        \         \        3,          ,          /# )r?   rw   r   rO   r   r   rP   r   )rC   s   "rD   rE   rE     sQ     4 444 4 	4
 4 
$sCx.4rF   c                   \         '       g|    ^ RIHp V! RV RVRVRV/4      p\         RV 2p\	        V4      p\
        P                  VR&   \
        P                  ! Wx^^R7      p	V	P                  R	V	P                  R
. 4      4      #  \         P                  ! \         R2RV RVRVRV/\	        V4      ^R7      pVP                  4        VP                  4       p	V	P                  R	V	P                  R
. 4      4      #   \         d   p
\        RT  RT
 24       . u Rp
?
# Rp
?
ii ; i  \         d   p
\        RT  RT
 24       . u Rp
?
# Rp
?
ii ; i)zSearch within a specific subreddit via ScrapeCreators.

Args:
    subreddit: Subreddit name (without r/)
    query: Search query
    token: ScrapeCreators API key
    sort: Sort order
    timeframe: Time filter

Returns:
    List of post dicts
r   rw   r   r   r   z/subreddit/search?r   r   r   r   z&Subreddit search error (urllib) for r/z: Nz/subreddit/searchr   zSubreddit search error for r/r   r   r   r   rS   r
   r   r   r   rM   r   r   )rw   r   rO   r   r   r   r   r   r   r   r   r   s   &&&&&       rD   _subreddit_searchr     s_   & 9	.Yk9  F )));F8DC!%(G$(OOGL!88C"aHD88GTXXfb%9::
}}"##45YY	  &

 	yy{xx&"!566%  	9)BqcJKI	&  ,YKr!=>	s=   A9C= 	A3D( =D%D D% D%(E3EEEc          	      t    V ^8  d   QhR\         R\         R\        \        \         \        3,          ,          /# )r?   r   rO   rP   r   )rC   s   "rD   rE   rE   K  s3     & &	&& 
$sCx.&rF   c                   \         '       gv    ^ RIHp V! RV /4      p\         RV 2p\	        V4      p\
        P                  VR&   \
        P                  ! WE^^R7      pVP                  RVP                  R. 4      4      #  \         P                  ! \         R
2RV /\	        V4      ^R7      pVP                  4        VP                  4       pVP                  RVP                  R. 4      4      #   \         d   p\        RT 24       . u R	p?# R	p?ii ; i  \         d   p\        RT 24       . u R	p?# R	p?ii ; i)zFetch comments for a Reddit post via ScrapeCreators.

Args:
    url: Reddit post URL or permalink
    token: ScrapeCreators API key

Returns:
    List of comment dicts with score, author, body, etc.
r   r   z/post/comments?r   r   commentsr   zComment fetch error (urllib): Nz/post/commentsr   zComment fetch error: r   )	r   rO   r   r   api_urlr   r   r   r   s	   &&       rD   fetch_post_commentsr   K  s(    9
	.s|,F,-_VHEG!%(G$(OOGL!88Gb!LD88J(<==
}}"#>23<&	
 	yy{xx
DHHVR$899  	1!56I	  $QC()	s=   A3C1 A-D 1D<DDDD>$D93D>9D>c                    V ^8  d   QhR\         \        \        \        3,          ,          R\         \        \        \        3,          ,          /# )r?   r   rP   r   r   rB   r   )rC   s   "rD   rE   rE   t  s4      d38n- $tCH~2F rF   c                R   \        4       p\        4       p. pV  F  pVP                  RR4      pVP                  RR4      pV'       d	   WQ9   d   K7  V'       d	   Wb9   d   KG  V'       d   VP                  V4       V'       d   VP                  V4       VP                  V4       K  	  V# )z9Deduplicate posts by reddit_id, keeping first occurrence.r   rv   r   )r   r   addrl   )r   seen_ids	seen_urlsuniquer   ridr   s   &      rD   _dedupe_postsr   t  s    uHIFhh{B'hhub!3?3#LLMM#d  MrF   c                    V ^8  d   QhR\         R\         R\         R\         R\         R\        \         \        3,          /# r?   rU   	from_dateto_daterj   rO   rP   rB   r   r   )rC   s   "rD   rE   rE     sT     ^  ^ ^ ^  ^  	^ 
 ^  
#s(^^ rF   c                
   V'       g   R. RR/# \         P                  V\         R,          4      pVR,          p\        W4      p\        RV  R\	        V4       RV 24       . pVR	,          p	\        VR
V	 4       Fe  w  rV
^ 8X  d   RMRp\        RV
^,            RV	 RV RV R2	4       \        WWR7      p\        R\	        V4       R24       VP                  V4       Kg  	  . p\        V4       F)  w  r\        W^,           R4      pVP                  V4       K+  	  \        WVR,          R7      p\        RV 24       \        V 4      pVR
VR,            F  p\        RV RV R24       \        VVVRVR7      p\        R\	        V4       RV 24       \        V4       F>  w  pp\        V\	        V4      V,           ^,           RV 24      pVP                  V4       K@  	  K  	  \        V4      p\        R\	        V4       R24       . p^ pV Fe  pVR ,          '       d-   VVR ,          u;8:  d   V8:  d   M MVP                  V4       K>  VR ,          f   VP                  V4       K\  V^,          pKg  	  V'       d   TpV'       d   \        R!V R"24       M\        R#\	        V4       24       VP                  R$ R%R&7       \        V4       F  w  p
pR'V
^,            2VR(&   K  	  \        R)\	        V4       R*24       RV/# )+a  Full Reddit search: multi-query global discovery + subreddit drill-down.

This is the main entry point. Replaces openai_reddit.search_reddit().

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 configuredr   r   z
Expanded 'z' into z
 queries: r   Nr   r   zGlobal search /z: 'z' (sort=))r   r   z  -> z resultsglobalr   )rU   rs   zDiscovered subreddits: zSubreddit search: r/z for ''z results from r/zr/zAfter dedup: z unique postsr   z	Filtered z posts outside date rangez(No posts within date range, keeping all c                 Z    V P                  R / 4      P                  R^ 4      ;'       g    ^ # )r   r~   r   )xs   &rD   <lambda>search_reddit.<locals>.<lambda>  s'    quu\2.227A>CC!CrF   Tkeyreverser   r   zFinal: z Reddit posts)DEPTH_CONFIGr   rp   rM   r\   	enumerater   extendr   rl   r   rh   r   r   r   )rU   r   r   rj   rO   configr   rn   all_raw_posts
max_globalir   r   r   	all_itemsr   itemdiscovered_subsrm   r   	sub_postsjin_rangeout_of_ranges   &&&&&                   rD   search_redditr     s   * W&LMMe\)%<=F{#I $E1G:eWGCL>G9	EF M)*Jgkz231f{%~acU!J<s5'$qIJu$LuSZL)*U# 4 I]+tUH5 ,
 *-vVjOklO"?"3	45 'D<'; <=#C5tfA67%c4[T]^	uS^$$4SE:; +GAt"4Y!);a)?2cULDT" ,	 > i(I=Y(	67 HL<<If@@OOD!&\!OOD!AL  	9\N*CDE7I7GHI NND   Y'41YT
 ( 	73y>"-	01YrF   c                    V ^8  d   QhR\         \        \        \        3,          ,          R\        R\        R\         \        \        \        3,          ,          /# )r?   r   rO   rj   rP   r   )rC   s   "rD   rE   rE     sP     K KS#XKK K 
$sCx.	KrF   c                l   \         P                  V\         R,          4      pVR,          pV '       d	   V'       g   V # V RV p\        R\        V4       R24       V EF  pVP                  RR4      pV'       g   K   \	        Wq4      pV'       g   K5  . p	. p
\        VR,          4       EFW  w  rVP                  R	R4      pV'       d   VR9   d   K)  VP                  R4      ;'       g    VP                  R^ 4      pVP                  RR
4      pVP                  RR4      pV'       d   RV 2MRpV^ 8X  d   RMRpV	P                  RVR\        VP                  R4      4      RVRVRV RV/4       \        V4      ^8  g   K  VR9  g   K  VR,          p\        V4      ^8  dK   \        V4       F$  w  ppVR9   g   K  V^28  g   K  VRV^,            p M	  VP                  4       R,           pV
P                  V4       EKZ  	  V	P                  R RR7       V	R,          VR&   V
R,          VR&   EK  	  V # )zEnrich top items with comment data from ScrapeCreators.

Args:
    items: Reddit items from search_reddit()
    token: ScrapeCreators API key
    depth: Depth for comment limit

Returns:
    Items with top_comments and comment_insights added.
r   r   NzEnriching comments for z postsr   rv   :N
   Nbody	[deleted]r}   r~   authorr   zhttps://reddit.comi  i,  r   r   excerpt:N   Nz.!?z...c                 &    V P                  R ^ 4      # )r~   r   )cs   &rD   r   &enrich_with_comments.<locals>.<lambda>/  s    gq(9rF   Tr   top_commentscomment_insights)r   	[removed])r   r  AutoModerator)
r   r   rM   r\   r   r   rl   r   r`   r   )r   rO   rj   r   max_comments	top_itemsr   r   raw_commentsr  insightscir  r   r~   r   r   comment_urlmax_excerptinsightr   chars   &&&                   rD   enrich_with_commentsr    s    e\)%<=F/0Lm|$I"3y>"2&	9:hhub!*36 |C01EB55$D4#==EE%L55AEE'1$5EUU8[1Fk2.I>G.yk:RK "$q#cKAEE-$89&4-{!  4yB61\#\t*t9s?#,W#545=QV&-dqsmG! $6
 #*.."2U":(= 2B 	94H+C0^#+C= c f LrF   c                    V ^8  d   QhR\         R\         R\         R\         R\         R\        \         \        3,          /# r   r   )rC   s   "rD   rE   rE   7  sL        	
  
#s(^rF   c                    \        WW#V4      pVP                  R. 4      pV'       d   V'       d   \        WdV4      pWeR&   V# )av  Full Reddit pipeline: search + comment enrichment.

This is the convenience function that does everything.

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. Items include top_comments and comment_insights.
r   )r   r   r  )rU   r   r   rj   rO   rg   r   s   &&&&&  rD   search_and_enrichr  7  s?    * 5WUCFJJw#E$U59wMrF   c                    V ^8  d   QhR\         \        \        3,          R\        \         \        \        3,          ,          /# )r?   responserP   )r   rB   r   r   )rC   s   "rD   rE   rE   V  s/     % %DcN %tDcN7K %rF   c                &    V P                  R. 4      # )zzParse ScrapeCreators response to item list.

Compatibility shim matching openai_reddit.parse_reddit_response() signature.
r   r   )r  s   &rD   parse_reddit_responser  V  s    
 <<$$rF   >	   
helpmefindfindaredditfindthatsongnamethatsongtipofmytonguewhatsthissongsubredditdramawhatisthissongwhatisthisthing)rv      )r   )r   r   )r   N)r   )&__doc__rerI   collectionsr   r   r   typingr   r   r   r   r	   requestsr   ImportErrorrv   r
   r   r   	frozensetr^   rM   rS   rh   rp   r   r   r   r   r   r   r   r   r   r  r  r  rR   rF   rD   <module>r*     s   
 
  ' 1 1  @  1aqV	 1aqW	 1aqW	,  
"$-/7%'0   % 	 	 $	
 
     %   * 
    " $) +1 	    " $* ,3   !  2<   .<b6,^4n&R(^ BK\>%A  Is   D$ $	D10D1