
    HE^jv                     	   U d dl Z d dlmZmZmZmZ d dlmZ d dlm	Z	m
Z
 d dlmZ d dlmZ 	 d dlZd dlmZ dZdaed
   ed<   d	aeed<   defdZddeded
   fdZded
   fdZ e       rd dlZdededdfdZdej:                  defdZdej:                  defdZ G d d
ej@                        Z! ejD                  dd       ejF                  ddddd d!d"d#d$d%&
       ejH                   ejJ                  d'd()       ejJ                  d*d+)       ejJ                  d,d-)       ejJ                  d.d/)       ejJ                  d0d1)       ejJ                  d2d3)       ejJ                  d4d5)       ejJ                  d6d7)      g8      	 	 	 	 	 	 	 	 	 	 ddej:                  d9ee&   d:ee'   d;ee'   d<ee&   d=ee&   d>ee&   d?eejP                     d@eejP                     dAeejP                     dBeejP                     fdC                     Z) ejD                  dDdE      dej:                  fdF       Z* ejD                  dGdH      dej:                  fdI       Z+ ejD                  dJdK      dej:                  fdL       Z, ejD                  dMdN      dej:                  fdO       Z- ejD                  dPdQ      dej:                  fdR       Z. ejD                  dSdT      dej:                  fdU       Z/ ejD                  dVdW       ejF                  dXY      dej:                  dZej`                  fd[              Z1 ejD                  d\d]       ejF                  d^Y      dej:                  dZej`                  fd_              Z2 ejD                  d`da      dej:                  fdb       Z3 ejD                  dcdd       ejF                  dedfg      	 ddej:                  dhe&diee'   fdj              Z4 ejD                  dkdl       ejF                  dmn      dej:                  dhe&fdo              Z5 ejD                  dpdq      dej:                  fdr       Z6dse7dte7defduZ8ddse7d>e&dvee'   ddwfdxZ9	 	 ddye:d>e&dvee'   dzee&   ddf
d{Z;	 dd|e:e7   d>e&dvee'   ddfd}Z<	 ddse7d>e&dvee'   ddfd~Z=ddhe&dSe&dee   ddfdZ>ddd
dee&ee&ef   f   deddfdZ?dhe&defdZ@y# e$ r d	ZY w xY w)    N)OptionalListDictAny)settings)_logcurrent_time)get_db_connection)get_product_cpu_gpu)app_commandsTFTrackFurbBot_discord_botSYNC_COMMANDSreturnc                  h    t        t        xr" t        j                  xr t        j                        S N)boolHAS_DISCORDr   DISCORD_BOT_TOKENOWNER_USER_ID     //home/container/backend/notifier/discord_bot.pyis_discord_enabledr      s#    U : :Ux?U?UVVr   sync_commandsc                 <    t               sy | at               at        S r   )r   r   r   r   )r   s    r   init_discord_botr      s    !M>Lr   c                      t         S r   )r   r   r   r   get_discord_botr      s    r   
channel_id
message_idc                 :  K   t         j                  syd|  d| d}dt         j                   dd}	 t        j                         4 d{   }|j	                  ||      4 d{   }|j
                  d	v rt        d
| d|         nn|j
                  dk(  rA	 |j                          d{   }|j                  dd      }t        d| d|  d| d       nt        d| d|  d|j
                          ddd      d{    ddd      d{    y7 7 7 o# t        $ r t        d| d|  d       Y Dw xY w7 <# 1 d{  7  sw Y   LxY w7 C# 1 d{  7  sw Y   yxY w# t        $ r}t        d| d|  d|        Y d}~yd}~ww xY ww)z_Publish/crosspost a message in an announcement channel using raw HTTP to avoid blocking on 429.Nz%https://discord.com/api/v10/channels/z
/messages/z
/crosspostzBot zapplication/json)AuthorizationzContent-Type)headers)      z)[Discord] Successfully published message  in channel   retry_afterr   z0[Discord] Rate limited (429) publishing message z. Retry after z"s. Skipping to avoid blocking bot.z!. Skipping to avoid blocking bot.z$[Discord] Failed to publish message z: HTTP z8[Discord] Error during non-blocking publish for message : )
r   r   aiohttpClientSessionpoststatusr   jsonget	Exception)	r    r!   urlr$   sessionrespdatar)   es	            r   _publish_message_non_blockingr7   $   s    ))5j\J<Wab#H$>$>#?@.
	w,,..'"<<W<=={{j0HT`ak`lmn+[)-#4D*.((=!*DK #ST^S__klvkw  xF  GR  FS  Su  "v  w CJ<|\f[ggnosozozn{|} >= /..=
 $5  ) [ #ST^S__klvkw  xY  "Z  [[ >=== /...  	wKJ<Wcdncooqrsqtuvv	ws   1FE1 DE1 E(D)E,0ED"0D 1+D"E;EEEE1 EE1 FE1 E D""E =E?E  EEE	EE	EE1 E."E%#E.*E1 -F.E1 1	F:FFFFinteractionc                 F   K    j                   j                  t        j                  k(  ry	  fd}t	        j
                  |       d {   }|ry	  j                  j                  dd       d {    y7 .# t        $ r}t        d|        Y d }~Dd }~ww xY w7 +w)NTc                     t               } 	 | j                         }|j                  dt        j                  j
                        f       |j                         }|d u| j                          S # | j                          w xY w)Nz8SELECT 1 FROM discord_authorized_users WHERE user_id = ?)r
   cursorexecutestruseridfetchonecloseconnr;   rowr8   s      r   db_opzcheck_authorized.<locals>.db_opB   sf    (*!![[]FNN#]`cdodtdtdwdw`x_z{ //+Cd?JJLDJJLs   AA1 1Bz[Discord] Auth check DB error: u/   ❌ You are not authorized to use this command.	ephemeralF)
r>   r?   r   r   asyncio	to_threadr1   r   responsesend_message)r8   rE   
authorizedr6   s   `   r   check_authorizedrM   >   s     ("8"88	8!  '0077J  ""//0amq/rrr 8  	821#677	8rsL   )B!A; 
A9A; "B!3B4B!9A; ;	BBB!BB!c                    K   | j                   j                  t        j                  k(  ry| j                  j                  dd       d {    y7 w)NTu4   ❌ Only the primary bot owner can use this command.rF   F)r>   r?   r   r   rJ   rK   )r8   s    r   check_ownerrO   S   sM     ("8"88""//0frv/www 	xs   A	AAAc                   @     e Zd Z fdZddZd Zd fdZddZ xZS )r   c                     t         j                  j                         }t        |   |       t        j                  |       | _        i | _        d | _	        t        j                         | _        y )N)intents)discordIntentsdefaultsuper__init__r   CommandTreetree_pending_edits_flush_taskrH   Lock
_edit_lock)selfrR   	__class__s     r   rW   zTrackFurbBot.__init__Z   sT    oo--/GGW-$006DI"$D#D%llnDOr   c                   K   | j                   j                  t               | j                   j                  t               | j                   j                  t               | j                   j                  t
               | j                   j                  t               | j                   j                  t               | j                   j                  t               | j                   j                  t               | j                   j                  t               | j                   j                  t               | j                   j                  t               | j                   j                  t               | j                   j                  t               t        r.| j                   j!                          d {    t#        d       y t#        d       y 7 w)Nz)[Discord] Slash commands synced globally.zL[Discord] Slash commands sync skipped. Use --sync-commands CLI flag to sync.)rY   add_commandsubscribe_commandunsubscribe_commandfilters_commandsubscriptions_commandscrape_commandclean_ghosts_commandstatus_commandauthorize_commanddeauthorize_commandauthorized_list_commandwatch_commandunwatch_commandwatchlist_commandr   syncr   r^   s    r   
setup_hookzTrackFurbBot.setup_hookb   s&    II!!"34II!!"56II!!/2II!!"78II!!.1II!!"67II!!.1II!!"34II!!"56II!!"9:II!!-0II!!/2II!!"34iinn&&&@Acd 's   F7G9G:Gc                 l   K   t        d| j                   d| j                  j                   d       y w)Nz[Discord] Bot ready as z (ID: ))r   r>   r?   rp   s    r   on_readyzTrackFurbBot.on_readyv   s*     *499+VDIILL>KLs   24c                    K   t        d       	 | j                          d {    t        |           d {    y 7 # t        $ r}t        d|        Y d }~5d }~ww xY w7 +w)NzP[Discord] Bot close requested. Flushing pending message edits before shutdown...z8[Discord] Error flushing pending edits during shutdown: )r   flush_pending_edits_nowr1   rV   rA   )r^   r6   r_   s     r   rA   zTrackFurbBot.closey   sg     cdU22444 '-/!! 5 UOPQsSTTU!sE   A'A ?A A'A%A'A 	A"
AA'A""A'c                   K   | j                   4 d{    | j                  s	 ddd      d{    y| j                  j                         }| j                  j                          | j                  r;| j                  j                         s!| j                  j                          d| _        ddd      d{    t        | d       d{    y7 7 7 # 1 d{  7  sw Y   /xY w7  w)z>Flush pending edits immediately, bypassing the cooldown delay.NT)shutdown)r]   rZ   copyclearr[   donecancel_perform_flush)r^   edits_to_processs     r   rv   z$TrackFurbBot.flush_pending_edits_now   s     ** ' $(#6#6#;#;#= ##))+##D,<,<,A,A,C$$++-'+D$ ' !'7$GGG ' Hsk   C5CC5CC5CC5A;C1C5<C=C5C3C5C5C5C0$C'%C0,C5)r   N)	__name__
__module____qualname__rW   rq   rt   rA   rv   __classcell__)r_   s   @r   r   r   Y   s    	-	e(	M	"	Hr   	subscribez2Subscribe current channel to Lenovo Outlet alerts.)namedescriptionzCComma-separated brands (e.g. thinkpad,legion,loq,ideapad,yoga,slim)z8Alert only if discount percentage is at least this valuez7Alert only if price is less than or equal to this valuez2Comma-separated keywords to search in product namez7Comma-separated conditions (e.g. certified refurbished)z1Select which alert events to send to this channelz:Discord role to ping when a product is added/back in stockzFDiscord role to ping when a product is temporarily unavailable/removedz3Discord role to ping when a product price decreasesz3Discord role to ping when a product price increases)
brandsmin_savings	max_pricekeywords
conditions
event_typeping_role_addedping_role_removedping_role_price_dropping_role_price_hikez*All Alerts (Added, Removed, Price Changes)all)r   valuez$Active Alerts (Added, Price Changes)activezRemoved Alerts Only (Sold Out)removedzAdded Alerts Only (New Stock)addedPrice Changes Onlyprice_changesRestocks & Price Drops Onlyadded_price_dropPrice Drops Only
price_dropPrice Hikes Only
price_hike)r   r   r   r   r   r   r   r   r   r   r   c                    	
K   t                d {   sy  j                  j                  d       d {    d|dk(  rdn/|dk(  rdn'|dk(  rdn|dk(  rd	n|d
k(  rdn|dk(  rdn|dk(  rd 	
fd}	 t        j                  |       d {    t        j                  dd j                   dt
        j                  j                               }|j                  dxs dd       |j                  d dndd       |j                  dddndd       |j                  dxs dd       |j                  dxs dd       d}|dk(  rd}n/|dk(  rd}n'|dk(  rd }n|dk(  rd!}n|d
k(  rd"}n|dk(  rd#}n|dk(  rd$}|j                  d%|d       r|j                  d&j                  d       r|j                  d'j                  d       	r|j                  d(	j                  d       
r|j                  d)
j                  d       |j                  d*+        j                  j                  |,       d {    y 7 U7 37 7 # t        $ rA}t!        d-|         j                  j                  d.| d       d {  7   Y d }~y d }~ww xY ww)/NTrF   r   r   added,price_drop,price_hiker   r   r   price_drop,price_hiker   added,price_dropr   r   c                     t               } 	 | j                         }r[dj                  j                  d      D cg c]1  }|j	                         s|j	                         j                         3 c}      nd }r[dj                  j                  d      D cg c]1  }|j	                         s|j	                         j                         3 c}      nd }r[dj                  j                  d      D cg c]1  }|j	                         s|j	                         j                         3 c}      nd }t               }rt        j                        nd }	rt        j                        nd }
rt        j                        nd }rt        j                        nd }|j                  dt        j                        t        j                        ||||	|
|||t        j                  j                        f       | j                          | j                          y c c}w c c}w c c}w # | j                          w xY w)N,a  
                    INSERT OR REPLACE INTO discord_subscriptions 
                    (channel_id, guild_id, min_savings, max_price, brands, keywords, conditions, event_types, 
                     ping_role_added_id, ping_role_removed_id, ping_role_price_drop_id, ping_role_price_hike_id, 
                     created_at, updated_by)
                    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                )r
   r;   joinsplitstriplowerr	   r=   r?   r<   r    guild_idr>   commitrA   )rC   r;   bclean_brandskclean_keywordscclean_conditionsnow
r_added_idr_removed_idr_price_drop_idr_price_hike_idr   r   db_event_typesr8   r   r   r   r   r   r   r   s                r   rE   z subscribe_command.<locals>.db_op   s   $&D"hnsxxFLLQTDU(cDUqYZY`Y`Yb):DU(cdtxlthnnUXFY*gFY]^]d]d]f1779??+<FY*g!hz~pz388
HXHXY\H],kH]1abahahajQWWY__->H],k#l  AE "n8GS!3!34T
<Ms#4#7#78SWBV#&:&=&=">\`BV#&:&=&=">\`   ../,,- "$" ##((++,, 

A )d*g,k< 

sG   0H; H, H,3)H; H12 H1)H; ;H6 H61C*H; ,H; ;Iu   ✅ Subscribed successfullyzAlerts configured for <#>.titler   colorBrands*Any*r   r   inlineMin Savings (%)%Max Price (INR)   ₹,.0fKeywords
Conditions
All Alerts#Active Only (Added & Price Changes)Removed Only (Sold Out)Added Only (New Stock)r   r   r   r   Event TypesPing (Added)Ping (Removed)Ping (Price Drop)Ping (Price Hike))   TrackFurb India • Lenovo Outlet Trackertextembedz[Discord] Failed to subscribe: u   ❌ Failed to subscribe: )rM   rJ   deferrH   rI   rS   Embedr    Colorgreen	add_fieldmention
set_footerfollowupsendr1   r   )r8   r   r   r   r   r   r   r   r   r   r   rE   r   et_descr6   r   s   `````` ````    @r   rb   rb      s    H &k222""((4(888!:N9$&N7"$N?*4N--/N<')N<')N$	 $	L+	]##E***MM36{7M7M6NbQmm))+E
 OO1B74OPOO!2{Of[M:Kls  }AO  BOO!2T]TiC	$?O:Pov  @DO  EOO83FwtOTOOZ5J7SWOX"GX%?y(3w&2..117|+,|+,OOgdOK^?;R;R[_` %5=N=V=V_cd#%8@T@\@\eij#%8@T@\@\eij"MN&&++%+888G 3 	9r +N 9 	]21#67&&++.Gs,KW[+\\\	]s}   K	I1$K	I4AK	I< 'I7(GI< +I:,I< 0K	4K	7I< :I< <	K1K6J97K<K	KK	unsubscribez6Unsubscribe current channel from Lenovo Outlet alerts.c                    K   t                d {   sy  j                  j                  d       d {     fd}	 t        j                  |       d {   }|r$ j
                  j                  d       d {    y  j
                  j                  d       d {    y 7 7 s7 R7 /7 # t        $ rA}t        d|         j
                  j                  d| d       d {  7   Y d }~y d }~ww xY ww)NTrF   c                     t               } 	 | j                         }|j                  dt        j                        f       | j                          |j                  dkD  }|| j                          S # | j                          w xY w)Nz6DELETE FROM discord_subscriptions WHERE channel_id = ?r   )r
   r;   r<   r=   r    r   rowcountrA   )rC   r;   deletedr8   s      r   rE   z"unsubscribe_command.<locals>.db_op#  sg    $&DWZ]^i^t^tZuYwx //A-



   AA4 4BuA   ❌ Channel unsubscribed. You will no longer receive alerts here.u5   ⚠️ This channel was not subscribed to any alerts.z![Discord] Failed to unsubscribe: u   ❌ Failed to unsubscribe: )	rM   rJ   r   rH   rI   r   r   r1   r   )r8   rE   r   r6   s   `   r   rc   rc     s     %k222""((4(888			_#--e44G!**//0sttt!**//0ghhh+ 3 	9 5th 	_4QC89&&++.I!,MY]+^^^	_s   D B)$D B+	D B3 B-$B3 ?B/ B3 D B3 #B1$B3 (D +D -B3 /B3 1B3 3	C=<1C8-C0.C83D 8C==D filtersz+Show active alert filters for this channel.c                    K   t                d {   sy  j                  j                  d       d {     fd}	 t        j                  |       d {   }|s$ j
                  j                  d       d {    y t        j                  dd j                   dt        j                  j                               }|j                  d	|j                  d
      xs dd       |j                  d|j                  d      |j                  d       dndd       |j                  d|j                  d      d|j                  d      dndd       |j                  d|j                  d      xs dd       |j                  d|j                  d      xs dd       |j                  d      xs d}d}|dk(  rd}n7|dk(  rd}n/|dk(  rd }n'|d!k(  rd"}n|d#k(  rd$}n|d%k(  rd&}n|d'k(  rd(}n|dk7  r|}|j                  d)|d       |j                  d*      r|j                  d+d,|d*    d-d       |j                  d.      r|j                  d/d,|d.    d-d       |j                  d0      r|j                  d1d,|d0    d-d       |j                  d2      r|j                  d3d,|d2    d-d       |j                  d45        j
                  j                  |6       d {    y 7 7 7 7 7 # t        $ rA}t!        d7|         j
                  j                  d8| d       d {  7   Y d }~y d }~ww xY ww)9NTrF   c                     t               } 	 | j                         }|j                  dt        j                        f       |j                         }|rt        |      nd 	 | j                          S # | j                          w xY w)Nz8SELECT * FROM discord_subscriptions WHERE channel_id = ?)r
   r;   r<   r=   r    r@   dictrA   rB   s      r   rE   zfilters_command.<locals>.db_op?  sg    $&DY\_`k`v`v\w[yzoo'$'tCyT1



s   AA3 3BuW   ❌ This channel is not subscribed to any alerts. Use `/subscribe` to configure alerts.u   🔍 Active Channel FilterszAlert settings for <#r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   event_typesr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   ping_role_added_idr   <@&>ping_role_removed_idr   ping_role_price_drop_idr   ping_role_price_hike_idr   r   r   r   z&[Discord] Failed to retrieve filters: u    ❌ Failed to retrieve filters: )rM   rJ   r   rH   rI   r   r   rS   r   r    r   bluer   r0   r   r1   r   )r8   rE   subr   etr   r6   s   `      r   rd   rd   8  s    %k222""((4(888	2	d))%00C!**//  1J  K  K  KMM33K4J4J3K2Nmm((*E
 OO1B1MgVZO[OO!2Z]ZaZaboZpZ|SWW]=S<TTU:V  CJ  SWO  XOO!2_b_f_fgr_s_C@TUY?Z:[  FM  VZO  [OO377:3F3Q'Z^O_OOSWW\5J5Ug^bOc'05B"G22?y3w2...))7|#,|#,uOOgdOKww+,^SEYAZ@[[\;]fjkww-.%5s3G]C^B__`=ajnoww01%8#cJcFdEeef@gptuww01%8#cJcFdEeef@gptu"MN&&++%+888} 3 	9 1 KX 9 	d9!=>&&++.Nqc,R^b+ccc	ds   ML $ML	ML L$L ?L	 L MI5L :L;L ?MML 	L L 	M1MM	MMMMsubscriptionsz2List all active subscriptions across all channels.c                   K   t        |        d {   sy | j                  j                  d       d {    d }	 t        j                  |       d {   }|s$| j
                  j                  d       d {    y t        j                  dt        j                  j                               }|D ]  }d|d    d	}g }|j                  d
      r|j                  d|d
    d       |j                  d      |j                  d|d    d       |j                  d      |j                  d|d   dd       |j                  d      r|j                  d|d    d       |j                  d      r|j                  d|d    d       |j                  d      xs d}d}|dk(  rd}n7|dk(  rd}n/|dk(  rd}n'|d k(  rd!}n|d"k(  rd#}n|d$k(  rd%}n|d&k(  rd'}n|dk7  r|}|j                  d(| d       |j                  d)      r|j                  d*|d)    d	       |j                  d+      r|j                  d,|d+    d	       |j                  d-      r|j                  d.|d-    d	       |j                  d/      r|j                  d0|d/    d	       d1j                  |      }	|j                  ||	d23        | j
                  j                  |4       d {    y 7 7 7 7 }7 # t        $ rA}
t!        d5|
        | j
                  j                  d6|
 d       d {  7   Y d }
~
y d }
~
ww xY ww)7NTrF   c                      t               } 	 | j                         }|j                  d       |j                         }|D cg c]  }t	        |       c}| j                          S c c}w # | j                          w xY w)N#SELECT * FROM discord_subscriptionsr
   r;   r<   fetchallr   rA   rC   r;   rowsrs       r   rE   z$subscriptions_command.<locals>.db_op  sb    $&DDE()-.AQ.

 /

   5A+ A&A+ &A+ +A=u6   ℹ️ No channels are currently subscribed to alerts.u   📋 All Active Subscriptionsr   r   z<#r    r   r   z	Brands: ``r   zMin Savings: `z%`r   u   Max Price: `₹r   r   zKeywords: `r   zConditions: `r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   zEvent Types: `r   zPing (Added): <@&r   zPing (Removed): <@&r   zPing (Price Drop): <@&r   zPing (Price Hike): <@&
Fr   r   z,[Discord] Failed to retrieve subscriptions: u&   ❌ Failed to retrieve subscriptions: )rM   rJ   r   rH   rI   r   r   rS   r   r   purpler0   appendr   r   r1   r   )r8   rE   subsr   r   channel_mentionr   r   r   filter_textr6   s              r   re   re   }  sl    %k222""((4(888	;	j **511D!**//0hiiiMM5mm**,E $&s<'8&9";778$NNYs8}oQ#?@77=)5NN^C4F3Gr#JK77;'3NN_S5Ed4K1#MN77:&NN[Z0A#CD77<(NN]3|3D2EQ#GHWW]+4u&66CG9_7G7]6G222G--;G<'0G<'0G5[ Gy:;77/0NN%6s;O7P6QQR#ST7712NN%8=S9T8UUV#WX7745NN%;C@Y<Z;[[\#]^7745NN%;C@Y<Z;[[\#]^"ii0_KPUVY \ &&++%+888O 3 	9 2ij 9 	j?sCD&&++.TUVTW,Xdh+iii	js   MK0$MK3MK> K6$K> <K9=K> MI(K> *K<+K> /M3M6K> 9K> <K> >	M1M8L;9M>MMMscrapez(Manually trigger a Lenovo Outlet scrape.c           	        K   t        |        d {   sy | j                  j                  d       d {    | j                  j	                  d       d {    	 ddlm} t        j                  |       d {   }|j                  d      r6| j                  j	                  d|d    d	|d
    d|d           d {    y | j                  j	                  d|j                  d              d {    y 7 7 7 7 7 C7 # t        $ r?}t        d|        | j                  j	                  d|        d {  7   Y d }~y d }~ww xY ww)NTrF   u;   🔄 Manual scrape started. Checking for product updates...r   )trigger_manual_scrapesuccessu-   ✅ Scrape finished successfully!
- Scanned: scanned_countz products
- Active in DB: active_countz
- Removed in DB: removed_countu   ❌ Scrape failed: errorz+[Discord] Failed in Discord manual scrape: u    ❌ Error during manual scrape: )rM   rJ   r   r   r   backend.mainr  rH   rI   r0   r1   r   )r8   r  resr6   s       r   rf   rf     s_    %k222""((4(888""''(efff	T:))*?@@Cwwy!!**//""%o"6!7 8''*>':&; <((+O(<'=?   "**//2EcgggFVEW0XYYY% 3 	9f
 A Z 	T>qcBC&&++.Nqc,RSSS	Ts   EC?$ED"EDED =D>AD DD E	0D 9D	:D >EEED D 	D 	E/EEE	EEEclean_ghostsz*Manually trigger a ghost listings cleanup.c           	        K   t        |        d {   sy | j                  j                  d       d {    | j                  j	                  d       d {    	 ddlm} t        j                  |       d {   }|j                  d      rF| j                  j	                  d|d    d	|d
   rdj                  |d
         nd        d {    y | j                  j	                  d|j                  d              d {    y 7 7 7 7 7 C7 # t        $ r?}t        d|        | j                  j	                  d|        d {  7   Y d }~y d }~ww xY ww)NTrF   u"   🔄 Cleaning up ghost listings...r   )trigger_ghost_cleanupr  u3   ✅ Ghost cleanup finished successfully!
- Cleaned cleaned_countz listings: cleaned_codesz, Noneu   ❌ Ghost cleanup failed: r	  z+[Discord] Failed in Discord ghost cleanup: u    ❌ Error during ghost cleanup: )rM   rJ   r   r   r   r
  r  rH   rI   r0   r   r1   r   )r8   r  r  r6   s       r   rg   rg     sk    %k222""((4(888""''(LMMM	T:))*?@@Cwwy!!**//!!$_!5 6kehixey$))CP_L`Ba  @F  BGH  
 "**//2LSWWU\M]L^0_```! 3 	9M
 A
 a 	T>qcBC&&++.Nqc,RSSS	Ts   E&D$E&D"E&DE&D =D>AD DD E&0D 	D
D E&E&E&D D D 	E#$/EEEE&E##E&r.   z;Show the tracker system health, uptime, and database count.c                    K   | j                   j                          d {    d }	 t        j                  |       d {   \  }}t	        j
                  dt        j                  j                               }|j                  ddd       |j                  dt        t        j                        d       |j                  d	t        |      d       |j                  d
t        |      d       |j                  d       | j                  j                  |       d {    y 7 7 7 
# t        $ r?}t!        d|        | j                  j                  d|        d {  7   Y d }~y d }~ww xY ww)Nc                     t               } 	 | j                         }|j                  d       |j                         d   }|j                  d       |j                         d   }||f| j	                          S # | j	                          w xY w)Nz.SELECT COUNT(*) FROM products WHERE active = 1r   z.SELECT COUNT(*) FROM products WHERE active = 0r
   r;   r<   r@   rA   )rC   r;   r   r   s       r   rE   zstatus_command.<locals>.db_op  sp    $&DOP*1-OP //+A.w



s   AA8 8B
u&   🖥️ TrackFurb India Tracker Statusr   z
Bot Statusu   🟢 Online & MonitoringTr   zHTTP Server PortzActive ProductszRemoved Productsr   r   r   z [Discord] Failed to get status: u'   ❌ Failed to retrieve status details: )rJ   r   rH   rI   rS   r   r   goldr   r=   r   PORTr   r   r   r1   r   )r8   rE   r   r   r   r6   s         r   rh   rh     sH    ""((***
		[$+$5$5e$<<OFGMM>mm((*E OO5OX\O]OO!33x}};MVZO[OO!2#f+dOSOO!33w<PTOU"MN&&++%+8883 	+ = 9 	[3A378&&++.UVWUX,YZZZ	[si   E>D,E>D3 D/C$D3 &D1'D3 +E>/D3 1D3 3	E;</E6+E.,E61E>6E;;E>	authorizezHWhitelist a user to manage alerts and run scraper commands (Owner only).zThe Discord user to authorize)r>   r>   c                    K   t                d {   sy  j                  j                  d       d {     fd}	 t        j                  |       d {     j
                  j                  dj                   dj                   d       d {    y 7 7 i7 G7 # t        $ rA}t        d|         j
                  j                  d| d       d {  7   Y d }~y d }~ww xY ww)	NTrF   c                  D   t               } 	 | j                         }t               }|j                  dt	        j
                        t	        j                  j
                        |f       | j                          | j                          y # | j                          w xY w)Nz^INSERT OR REPLACE INTO discord_authorized_users (user_id, added_by, added_at) VALUES (?, ?, ?))	r
   r;   r	   r<   r=   r?   r>   r   rA   )rC   r;   r   r8   r>   s      r   rE   z authorize_command.<locals>.db_op#  sr    $&D	"nt\3{'7'7':':#;SA 



s   A/B Bu	   ✅ User z (`z`) is now authorized.z$[Discord] Failed to authorize user: u   ❌ Failed to authorize user: )rO   rJ   r   rH   rI   r   r   r   r?   r1   r   )r8   r>   rE   r6   s   ``  r   ri   ri     s      !---""((4(888		b##E***&&++i~S	Qf,ghhh) . 	9  +h 	b7s;<&&++.LQC,P\`+aaa	bsy   C5B $C5B"
C5B( B$=B( B&B( C5"C5$B( &B( (	C211C-"C%#C-(C5-C22C5deauthorizez1Revoke authorized access for a user (Owner only).zThe Discord user to deauthorizec                 <  K   t        |        d {   sy | j                  j                  d       d {    fd}	 t        j                  |       d {   }|r2| j
                  j                  dj                   d       d {    y | j
                  j                  dj                   d       d {    y 7 7 7 n7 =7 # t        $ rA}t        d|        | j
                  j                  d	| d       d {  7   Y d }~y d }~ww xY ww)
NTrF   c                     t               } 	 | j                         }|j                  dt        j                        f       | j                          |j                  dkD  }|| j                          S # | j                          w xY w)Nz6DELETE FROM discord_authorized_users WHERE user_id = ?r   )r
   r;   r<   r=   r?   r   r   rA   )rC   r;   r   r>   s      r   rE   z"deauthorize_command.<locals>.db_op?  sg    $&DWZ]^b^e^eZfYhi //A-



r   u"   ❌ Revoked authorized access for .u   ⚠️ User z was not authorized.z&[Discord] Failed to deauthorize user: u    ❌ Failed to deauthorize user: )
rO   rJ   r   rH   rI   r   r   r   r1   r   )r8   r>   rE   r   r6   s    `   r   rj   rj   7  s     !---""((4(888			d#--e44G!**//2TUYUaUaTbbc0deee!**//,t||nL`0abbb+ . 	9 5eb 	d9!=>&&++.Nqc,R^b+ccc	ds   DC$DC	DC C	2C CC D,C ?C C DD	C C C 	D1D	D
DDDDauthorized_listz3List all whitelisted authorized users (Owner only).c           
        K   t        |        d {   sy | j                  j                  d       d {    d }	 t        j                  |       d {   }t        j                  ddt        j                   dt
        j                  j                               }g }|D ]&  }|j                  d|d	    d
|d    d|d    d       ( |xj                  d|rdj                  |      ndz   z  c_        | j                  j                  |       d {    y 7 7 7 7 # t         $ r}t#        d|        Y d }~y d }~ww xY ww)NTrF   c                      t               } 	 | j                         }|j                  d       |j                         }|D cg c]  }t	        |       c}| j                          S c c}w # | j                          w xY w)Nz&SELECT * FROM discord_authorized_usersr   r   s       r   rE   z&authorized_list_command.<locals>.db_op[  sb    $&DGH()-.AQ.

 /

r   u   🔒 Authorized Users ListzPrimary Owner: <@r   r   u   • <@user_idz> (Added by <@added_byz> on added_atrs   

r   z No additional users whitelisted.r   z+[Discord] Failed to list authorized users: )rO   rJ   r   rH   rI   rS   r   r   r   r   	dark_greyr   r   r   r   r   r1   r   )r8   rE   usersr   
user_linesur6   s          r   rk   rk   T  sJ     ---""((4(888		D!++E22EMM2/0F0F/GqImm--/E
 J!!F1Y<.q}oUZ[\]g[hZiij"kl  J499Z+@Tv!ww&&++%+8887 . 	9 3 9 	D>qcBCC	Dsm   ED$ED"ED( D$CD( D&D( E"E$D( &D( (	E	1E?EE		Ewatchz<Watch a specific product code for stock/price notifications.z+The Lenovo product number (e.g. 83DV007GIN)z4Alert only if price drops below or equals this value)product_codetarget_pricer*  r+  c                    K   t         j                  s& j                  j                  dd       d {    y t	                d {   sy  j                  j                  d       d {    j                         fd}t        j                  |       d {   }|r|d   nd}|r|d   nd } fd}	 t        j                  |       d {    d	d
nd}d| d d| d}	|
|	d|d
dz  }	 j                  j                  |	       d {    y 7 7 7 7 7 R7 # t        $ rD}
t        d d|
         j                  j                  d|
 d       d {  7   Y d }
~
y d }
~
ww xY ww)NM   ❌ The personal watchlist feature is globally disabled by the administrator.TrF   c                      t               } 	 | j                         }|j                  df       |j                         | j	                          S # | j	                          w xY w)NzGSELECT product_name, current_price FROM products WHERE product_code = ?r  )rC   r;   r*  s     r   	db_verifyz watch_command.<locals>.db_verify  sK    $&Dhkwjyz(



s   2A A"r   Unknown Product   c                      t               } 	 | j                         }t               }|j                  dt	        j
                  j                        |f       | j                          | j                          y # | j                          w xY w)Nz
                    INSERT OR REPLACE INTO watchlists (user_id, product_code, target_price, created_at)
                    VALUES (?, ?, ?, ?)
                )	r
   r;   r	   r<   r=   r>   r?   r   rA   )rC   r;   r   r8   r*  r+  s      r   rE   zwatch_command.<locals>.db_op  sq    $&D	"n   +**--.lCPR 



s   AA; ;Bu    when price is ≤ ₹r    u   ✅ Now watching **z** (`z`)z..
You will receive a DM when triggers are met.u   
*Current Price in DB: ₹*z"[Discord] Failed to watch product r*   u&   ❌ Failed to register watch trigger: r   WATCHLIST_ENABLEDrJ   rK   rM   r   r   rH   rI   r   r   r1   r   )r8   r*  r+  r/  product_rowproduct_name
curr_pricerE   price_clausemsgr6   s   ```        r   rl   rl   u  s     ))&&33  5D  PT3  U  U  U%k222""((4(888#))+	 $--i88)4{1~:K'2[^
			j##E***KWKc3L3FGikL'~U<.<.  YH  IC%4Z4EQGG&&++C000W U 3 	9 9$ +
 1 	j5l^2aSIJ&&++.TUVTW,Xdh+iii	js   1E?D#E?
D%$E?/D'01E?!D)"E? D/ D+AD/ D-D/ "E?%E?'E?)E?+D/ -D/ /	E<84E7,E/-E72E?7E<<E?unwatchz&Stop watching a specific product code.z*The Lenovo product number to stop watching)r*  c                    K   t         j                  s& j                  j                  dd       d {    y t	                d {   sy  j                  j                  d       d {    j                          fd}	 t        j                  |       d {   }|r( j                  j                  d d       d {    y  j                  j                  d d       d {    y 7 7 7 7 \7 57 # t        $ rD}t        d d	|         j                  j                  d
| d       d {  7   Y d }~y d }~ww xY ww)Nr-  TrF   c                  $   t               } 	 | j                         }|j                  dt        j                  j
                        f       | j                          |j                  dkD  | j                          S # | j                          w xY w)Nz=DELETE FROM watchlists WHERE user_id = ? AND product_code = ?r   )	r
   r;   r<   r=   r>   r?   r   r   rA   )rC   r;   r8   r*  s     r   rE   zunwatch_command.<locals>.db_op  sv    $&D^adepeueuexexay  |H  aI  J*



s   AA= =Bu   ❌ Stopped watching product `z`.u&   ⚠️ You were not watching product `z$[Discord] Failed to unwatch product r*   u$   ❌ Failed to remove watch trigger: r5  )r8   r*  rE   r   r6   s   ``   r   rm   rm     sb     ))&&33  5D  PT3  U  U  U%k222""((4(888#))+		h#--e44G!**//2PQ]P^^`0abbb!**//2XYeXffh0ijjj1 U 3 	9 5bj 	h7~RsKL&&++.RSTRU,Vbf+ggg	hs   1EC9E	C;
$E.C=/E
D "C?#(D DD E"D 3D4D 8E;E=E?D D D 	E4EEEEEE	watchlistz:List all product watch triggers you are currently running.c                 N   K   t         j                  s& j                  j                  dd       d {    y t	                d {   sy  j                  j                  d       d {     fd}	 t        j                  |       d {   }|s$ j                  j                  d       d {    y t        j                  dt        j                  j                               }|D ]  }|j                  d      xs d	}|d
   }|j                  d      }|j                  d      }|j                  d      }	|j                  d      }
|	dk(  rd}n|
d}nd}|d|dnd}|d|dnd}d| d| d| d| d|d    
}|j                  d| d|d          j                  j                  |!       d {    y 7 7 7 `7 @7 7 # t         $ rA}t#        d"|         j                  j                  d#| d       d {  7   Y d }~y d }~ww xY ww)$Nr-  TrF   c                  <   t               } 	 | j                         }|j                  dt        j                  j
                        f       |j                         D cg c]  }t        |       c}| j                          S c c}w # | j                          w xY w)Na7  
                    SELECT w.product_code, w.target_price, w.created_at, p.product_name, p.current_price, p.active, p.pending_removal_since
                    FROM watchlists w
                    LEFT JOIN products p ON w.product_code = p.product_code
                    WHERE w.user_id = ?
                )	r
   r;   r<   r=   r>   r?   r   r   rA   )rC   r;   r   r8   s      r   rE   z watchlist_command.<locals>.db_op  s    $&D
  
 +**--.02 *0):;):AQ):;

 <

s   AB	 B1B	 B	 	BuI   ℹ️ You are not watching any products. Use `/watch` to start watching.u&   ❤️ Your Personal Product Watchlistr   r8  r0  r*  r+  current_pricer   pending_removal_sincer1  u   🟢 Activeu,   🟡 Temporarily Reserved (Pending Sold Out)u   🔴 Temporarily Unavailabler   r   z*Any Price Drop*N/A**u   **
• Status: u   
• Current Price: u   
• Target Price: u   
• Watched Since: 
created_atzCode: `r   Fr   r   z$[Discord] Failed to list watchlist: u"   ❌ Failed to retrieve watchlist: )r   r6  rJ   rK   rM   r   rH   rI   r   r   rS   r   r   redr0   r   r1   r   )r8   rE   itemsr   itemr   codetargetcurrr   pending_removalr.   
target_strcurr_strdescr6   s   `               r   rn   rn     sh    ))&&33  5D  PT3  U  U  U%k222""((4(888	'	f!++E22E!**//0{|||MM>mm'')E xx/D3DN+.1xx0(+"&((+B"CQ;*F$0KF;F4:4Fs6$-0L^
040@Sd,e  ##)( +**2 4))3 5**.|*<)=	?  wtfA%6d5Q1 4 &&++%+888u U 3 	9" 3|B 9 	f7s;<&&++.PQRPS,T`d+eee	fs   1H%GH%G
	$H%-G.	H%8G G$G 5G6G :H%;DG GG H%
H%H%G G G 	H"!1HHHH%H""H%productr   c                    | j                  d      }|| j                  d      }|	 t        |      }nd}|d   }|||kD  ry| j                  d      }|| j                  d      }|	 t        |      }nd}|d   }|||k  ry|d	   }|r| j                  d
      xs dj	                         | j                  d      xs dj	                         |j                  d      D cg c]1  }|j                         s|j                         j	                         3 }}|D cg c]  }|j                  d      r| }	}|D cg c]  }|j                  d      s|dd   }
}|
rt        fd|
D              ry|	rt        fd|	D              sy|d   }|r| j                  d
      xs dj	                         | j                  d      xs dj	                         |j                  d      D cg c]1  }|j                         s|j                         j	                         3 }}|D cg c]  }|j                  d      r| }}|D cg c]  }|j                  d      s|dd   }}|rt        fd|D              ry|rt        fd|D              sy|d   }|r| j                  d      xs | j                  d      xs dj	                         }|j                  d      D cg c]1  }|j                         s|j                         j	                         3 }}|r||vryy# t        t        f$ r d}Y w xY w# t        t        f$ r d}Y w xY wc c}w c c}w c c}w c c}w c c}w c c}w c c}w )N
finalPricerB          r   FsavePercentsave_percentr   r   productNamer3  modelr   !r1  c              3   2   K   | ]  }|v xs |v   y wr   r   .0brand
model_namer8  s     r   	<genexpr>z(_matches_subscription.<locals>.<genexpr>?  s&     cXbue|3Ju
7JJXb   c              3   2   K   | ]  }|v xs |v   y wr   r   r[  s     r   r_  z(_matches_subscription.<locals>.<genexpr>B  s'     !g\fSX%<"7"N5J;N"N\fr`  r   c              3   2   K   | ]  }|v xs |v   y wr   r   r\  kwr^  r8  s     r   r_  z(_matches_subscription.<locals>.<genexpr>N  s&     ^Q]2l 2 FbJ6F FQ]r`  c              3   2   K   | ]  }|v xs |v   y wr   r   rc  s     r   r_  z(_matches_subscription.<locals>.<genexpr>Q  s&     #bUarB,$6$J"
:J$JUar`  r   productCondition	conditionT)	r0   float
ValueError	TypeErrorr   r   r   
startswithany)rQ  r   pricer   savingsr   
sub_brandsr   
brand_list
pos_brands
neg_brandsr   r   keyword_listpos_keywordsneg_keywordssub_conditionsrg  r   	cond_listr^  r8  s                       @@r   _matches_subscriptionrx    sO   KK%E}O,	%LE K I!2kk-(G++n-	GnG m$K7[#8XJM28b??Akk'*0b779
1;1A1A#1FT1FA!'')aggioo'1F
T!+EA1<<3Da
E%/EZ1<<3DaeZ
E #cXbccc!g\f!gg:HM28b??Akk'*0b779
3;>>#3FT3Fa!'')	)3FT#/I<aq||C7H<I'3I|!q||C7H!"|I C^Q]^^#bUa#b b&N[[!34VK8PVTV]]_	0>0D0DS0IW0I1QWWYQWWY__&0I	W)3{ I& 	E	 I& 	G	 UEE UII Xsw   L3 ,M (M#> M#$M(;M(M-M-6M2 M22M7	M7M<*M<3N	 N3MMM M 	old_pricezdiscord.Embedc           	      	   t         j                  j                         t         j                  j                         t         j                  j	                         t         j                  j                         d}ddddd}| j                  d      xs | j                  d      xs d}| j                  d	      xs | j                  d
      xs d}| j                  d      }|| j                  d      }| j                  d      }|| j                  d      }| j                  d      xs# | j                  d      xs t        j                  }	| j                  d      xs d| }
|
j                  d      r|
}n'|
rt        j                   |
 }nt        j                  }| j                  d      }|s| j                  di       }|j                  d      xs i }|j                  d      xs i }|j                  dg       }t        |t              r#|j                  d      r|j                  d      }nyt        |t              r#|j                  d      r|j                  d      }nF|rDt        |t              r4t        |      dkD  r&|d   }t        |t              r|j                  d      }|r|j                  d      rd|z   }t        j                   |j                  |d       d| d|j                  |t         j                  j#                               |       }d!}|	 d"t%        |      d#}|j-                  d$|d%&       |$	 d"t%        |      d#}|j-                  d'|d%&       |j-                  d(|| d)nd*d%&       |j-                  d+|	d%&       |j-                  d,d-| d-d%&       t/        |       \  }}|j-                  d.|d%&       |j-                  d/|d%&       |d0k(  r(| j                  d1      }|rA|j-                  d2|d%&       n,|d3k(  r'| j                  d4      }|r|j-                  d5|d%&       |r|j1                  |6       |j3                  d78       t         j4                  j7                         |_        |S # t&        t(        f$ r t+        |      }Y xw xY w# t&        t(        f$ r t+        |      }Y sw xY w)9Nr   r   r   r   "   🆕 Product Added / Back in Stock#   ❌ Product Temporarily Unavailable   📉 Price Drop Alert   📈 Price IncreaserW  r8  r0  productCoder*  ?rS  rB  rU  rV  rf  rg  r2   /p/httpthumbnail_urlmedia	heroImage	thumbnailgalleryimageAddressr   z//zhttps:
   🔔 AlertrE  )r   r   r   r2   rD  r   r   u
   💰 PriceTr      📉 Old Priceu   🏷️ Savingsr   0%u   📦 Conditionu   🔗 Product Coder   u
   ⚙️ CPUu   🎮 GPUr   _listing_duration   ⏱️ Listed forr   _restock_duration   ⏱️ Restocked after)r2   r   r   )rS   r   r   rG  r   r  r0   r   DEFAULT_CONDITIONrk  LENOVO_BASE_URLLENOVO_LAPTOPS_URL
isinstancer   listlenr   rU   rh  ri  rj  r=   r   r   set_thumbnailr   utilsutcnow	timestamp)rQ  r   ry  colorstitlesr   rJ  rm  rn  rg  url_pathr2   r  r  herothumbr  rI  r   	price_strold_price_strcpugpudurationrestock_durations                            r   _build_product_embedr  ^  s\   $$&==$$&mm((*mm((*	F 68-+	F ;;}%Y^)DYHYD;;}%K^)DKDKK%E}O,kk-(G++n-./i7;;{3KixOiOiI{{5!1s4&\H6"	))*8*5))O,IGR(yy%+		+&,"))Ir*dD!dhh~&>0It$>)B		.1IGT2s7|a7G1:D$% HH^4	Y))$/y(	MMJ56bMjjW]]%:%:%<=	E I	#eEl401I 
OOYtOD	+!%	"24!89M 	-]4P	OO*7CVWIQ-\`imOn	OO)4OH	OO,avQKOM"7+HC	OOSO>	OO3tO<Y;;23OO!4HTOR	w	";;':;OO!9AQZ^O_	*	EFmm**,EOLG I& 	#E
I	# I& 	+	NM	+s$   $R 
R/ R,+R,/SSproductsping_role_idc                 p  K   |rd| dnd}| d}g }ddddd	}|j                  |d
      }	|D ]  }
|
j                  d      xs |
j                  d      xs d}|
j                  d      xs |
j                  d      xs d}|
j                  d      }||
j                  d      }|
j                  d      }||
j                  d      }|
j                  d      xs d| }|j                  d      r|}n'|rt        j                   | }nt        j                  }d}|	 dt        |      d}|d| dnd}|dv r|	 dt        |      d}| d | d!}d}|d"k(  r|
j                  d#      }|r#d$| }n|d%k(  r|
j                  d&      }|rd'| }t        |
      \  }}|j                  d(|	 d)| d*| d+| d,| | d-| d.| d/| d!        |}|D ]  }t        |      t        |      z   d0z   d1kD  ru| j                  |       d{   }| j                  t        j                  j                   k(  r3t#        j$                  t'        | j(                  |j(                               d}||d2z   z  } |ru| j                  |       d{   }| j                  t        j                  j                   k(  r4t#        j$                  t'        | j(                  |j(                               yyy# t        t        f$ r t        |      }Y w xY w# t        t        f$ r t        |      }Y w xY w7 %7 w)3z[Format and send product alerts as a compact summary message, handling 2000 character limit.r   z> r3  uC   🔔 **TrackFurb India: Multiple Product Alerts (Channel Digest)**
u
   🆕 Addedu   ❌ Temporarily Unavailableu   📉 Price Dropu   📈 Price Hiker{  r  rW  r8  r0  r  r*  r  rS  NrB  rU  rV  r2   r  r  rD  r   r   z	Savings: r   r  )r   r   z (Was: rs   r   r  u    — Listed for: r   r  u    — Restocked after: u   • [z] z (u   ) — u    — u
   
  ⚙️ u    | 🎮 u    — [Store Link](   i  r   )r0   rk  r   r  r  rh  ri  rj  r=   r   r   r  r   typerS   ChannelTypenewsrH   create_taskr7   r?   )channelr  r   ry  r  ping_prefixheaderlinesevent_actionsactionpr   rJ  rm  rn  r  r2   r  savings_strr  duration_strr  r  r  r  current_msgliner;  s                               r   _send_compact_alertsr    s     -9C~R(bK}`aFE 0''	M z<8Fuu]#Qquu^'<Q@Quu]#Cquu^'<Cl#=EE/*E%%&?eeN+G55</S<v&C--.xj9C--C	'!%,t!45	 180C	'!,55):O/"%eI&6t%< = %+W]O1=I"uu01H!28*=7" uu%89!78H7IJ&q)SuVHBtfBtfF9+U;-XdWeepqtpuu}  B  ~C  CU  VY  UZ  Z[  \  	]_ d K{c$i'!+d2[11C||w22777##$A'**cff$UVKtd{"  LL--<<7..333 =gjj#&& QR 4 G 	* 'J	' 	* / #I/* 2 .so   D L6#K/2L6LB:L6L1B L6L4A!L6/L	L6LL6L.*L6-L..L64L6queuec                    t               rt        rt        j                         sy	 t               }	 |j	                         }|j                  d       |j                         D cg c]  }t        |       }}|j                          	 |D ]  }|j                  dd      xs d}	|	dk7  rB|	j                  d      D 
ch c]#  }
|
j                         s|
j                         % }}
|vrag }| D ]   }t        ||      s|j                  |       " |s|d   }d}dk(  r|j                  d	      }nDd
k(  r|j                  d      }n-dk(  r|j                  d      }ndk(  r|j                  d      }|||ffd	}t!        j"                   |       t        j$                         " yc c}w # |j                          w xY w# t        $ r}t        d|        Y d}~yd}~ww xY wc c}
w )zKRoute product events to subscribed Discord channels based on their filters.Nr   z0[Discord] Database error reading subscriptions: r   r   r   r    r   r   r   r   r   r   r   r   c           
        K   	 t         j                  t        |             }|s&t         j                  t        |              d {   }|sy d}|d | }||d  }d}t	        dt        |      d      D ]?  }|||dz    }	g }
|	D ]  }	 |
j                  t        |             ! |
s5d }|r
|sd| d}d}	 |j                  ||
       d {   }|j                  t        j                  j                  k(  r3t        j                   t#        |j$                  |j$                               d	 }|	D cg c]&  }|j'                  d
      xs |j'                  d      ( }}t        j                   t        j(                  ||j$                  ||              t        j*                  d       d {    B |rt3        |||rd n|       d {    y y 7 # t        $ r}t        d|        Y d }~rd }~ww xY w7 0c c}w 7 V# t        j,                  j.                  $ ra}|j0                  dk(  r6t        d|  d       ||d  }t3        |||rd n|       d {  7   Y d }~ y t        d|  d|        Y d }~d }~wt        $ r}t        d|  d|        Y d }~0d }~ww xY w7 # t        $ r}t        d|  d|        Y d }~y d }~ww xY ww)N
   Fr   z,[Discord] Error building embed for product: r   r   T)contentembedsc           	      .   dd l }t               }	 |j                         }|j                  d      }|D ]+  }|j	                  d|t        |      t        |       ||f       - |j                          |j                          y # |j                          w xY w)Nr   z%Y-%m-%d %H:%M:%SaM  
                                            INSERT OR REPLACE INTO sent_messages
                                            (platform, product_code, target_id, message_id, event_type, message_type, sent_at)
                                            VALUES ('discord', ?, ?, ?, ?, NULL, ?)
                                        )timer
   r;   strftimer<   r=   r   rA   )	msg_idpr_codescidev_typer  rC   r;   now_strrJ  s	            r   save_sent_msg_idszKdispatch_discord_alerts.<locals>.send_to_channel.<locals>.save_sent_msg_idsq  s     +'8':!1-1[[]F.2mm<O.PG08(. 8, /3CHc&k7T[-\	)^ 19 %)KKM$(JJLDJJLs   A!B Br  r*        ?r(   z9[Discord] Rate limit hit (429) sending embeds to channel z!. Switching remainder to compact.z5[Discord] HTTP error sending embeds batch to channel r*   z0[Discord] Error sending embeds batch to channel z2[Discord] Failed to send notifications to channel )r   get_channelintfetch_channelranger  r   r  r1   r   r   r  rS   r  r  rH   r  r7   r?   r0   rI   sleeperrorsHTTPExceptionr.   r  )r  r  r  r  max_embeds_countembed_productscompact_products	ping_sentibatchr  rQ  r6   r  r;  r  r  r  he	remainderexr   ry  s                        r   send_to_channelz0dispatch_discord_alerts.<locals>.send_to_channelN  s!    EW&223s8<$0$>$>s3x$HHG $& !)*;+;!<#+,<,=#> !	q#n"5r:A*1QrT2EF#(U"MM*>w
T]*^_ $) "&'	(+L>&;G(,I#a(/WV(T"TC&||w/B/B/G/GG ' 3 34QRYR\R\^a^d^d4e f1  `e'e_dZ[m(<(Un@U(U_dH'e#//0A0ABSUXU[U[]egjlv0wx")--"444Q ;h $.w8H*V_irae  yE  F  F  F $} I   ) U #OPQs!STTU #U* (f 5&~~;; j!yyC/ $'`ad`e  fG  &H  !I,4QRL	&:7Iz[dnwfj  ~J  'K  !K  !K & $'\]`\aacdfcg%h i i( a #STWSXXZ[]Z^!_``a F WI#bQSPTUVVWs  KAJ, GJ, K7J, G"J, 'J, 6HG<A%H2+G?AH-H.H2J, J*J, KJ, 	G9 G4.J, 4G99J, <H?HJ'#9I?II?"J, 'K(I?9J, ?J'J"J, "J''J, ,	K5KKKK)r   r   is_readyr
   r;   r<   r   r   rA   r1   r   r0   r   r   rx  r   rH   run_coroutine_threadsafeloop)r  r   ry  rC   r;   r   r   r6   r   event_types_strr   allowed_eventsmatching_productsrQ  r  r  r  s    ``              r   dispatch_discord_alertsr    s    |<;P;P;R
 "	[[]FNN@A.4oo.?@.?T!W.?M@JJL
 ''-7@5e#3B3H3H3M\3MRQSQYQYQ[bhhj3MN\/ G$Wc2!((1  !,  77#78L9$77#9:L<'77#<=L<'77#<=L '*4ET` F	WP 	(():L<M<MNM  AJJL ?sCD ]sL   
G 3F7 'F29F7 ;G  G0G02F7 7G		G 	G-G((G-c                 6   t         j                  r$t               rt        rt        j	                         sy| j                  d      xs | j                  d      }|sy| j                  d      }|| j                  d      }	 |t        |      nd}	 t               }	 |j                         }|j                  d|f       |j                         }|j                          	 |D ]d  }	|	d	   }
|	d
   }|||kD  rt!        | ||      }d|j"                   |_        |
|fd}t%        j&                   |       t        j(                         f y# t        t        f$ r d}Y w xY w# |j                          w xY w# t        $ r}t        d|        Y d}~yd}~ww xY w)zLCheck watchlist triggers and send direct message alerts to users if matched.Nr  r*  rS  rB  rT  zCSELECT user_id, target_price FROM watchlists WHERE product_code = ?z$[Discord Watchlist] Database error: r   r1  u   ❤️ Watchlist Alert: c                 p  K   	 t         j                  t        |             }|s&t         j                  t        |              d {   }|r|j	                  |       d {    y y 7 "7 # t
        j                  $ r t        d|  d       Y y t        $ r}t        d|  d|        Y d }~y d }~ww xY ww)Nr   z+[Discord Watchlist] Cannot send DM to user z( (DMs disabled by user privacy settings)z.[Discord Watchlist] Failed to send DM to user r*   )	r   get_userr  
fetch_userr   rS   	Forbiddenr   r1   )uidembr>   r  s       r   send_dmz*dispatch_watchlist_alerts.<locals>.send_dm  s     	S#,,SX6!-!8!8S!BBD))#)...  C.$$ rB3%Gopq SEcU"RDQRRSs_   B6AA, A(A, !A*"A, &B6(A, *A, ,"B3B6B3B.)B6.B33B6)r   r6  r   r   r  r0   rh  ri  rj  r
   r;   r<   r   rA   r1   r   r  r   rH   r  r  )rQ  r   ry  rJ  rm  rC   r;   matchesr6   matchr!  r+  r   r  s                 r   dispatch_watchlist_alertsr    s    %%-?-A]i]r]r]t;;}%D^)DDKK%E}O, % 1es
 "	[[]FNN`cgbijoo'GJJL
 (Qx #(< %Wj)D0>%5 
	S 	((L4E4EF1  	"  JJL 3A378sB   E 
E7 3E" E7 EE"E44E7 7	F FFexpire_epochc                      t               rt        rt        j                         sy fd}t        j                   |       t        j
                         y)zATrigger non-blocking Discord message edits for state transitions.Nc                  f  K   t         j                  4 d {    dt         j                  <   t         j                  t         j                  j	                         r&d } t        j                   |              t         _        d d d       d {    y 7 7 # 1 d {  7  sw Y   y xY ww)N)r.   r  c                    K   t        j                  d       d {    t        j                  4 d {    t        j                  j                         } t        j                  j                          d t        _        d d d       d {     rt        t        |        d {    y y 7 7 y7 $# 1 d {  7  sw Y   4xY w7 !w)Ng      ?)	rH   r  r   r]   rZ   ry   rz   r[   r}   )r~   s    r   delayed_flushzIdispatch_discord_edit.<locals>.buffer_and_schedule.<locals>.delayed_flush  s     !--,,,+666+7+F+F+K+K+M($3399;370  76 (,\;KLLL ( -66666
 Msa   C	B,C	B.C	AB2 C	B0C	%C&C	.C	0C	2C8B;9C C	)r   r]   rZ   r[   r{   rH   r  )r  r  r*  r.   s    r   buffer_and_schedulez2dispatch_discord_edit.<locals>.buffer_and_schedule  s     *** ! ,9L''5 ''/<3K3K3P3P3RM ,3+>+>}+O(' +******sE   B1BB1A+BB1BB1B1B."B%#B.*B1)r   r   r  rH   r  r  )r*  r.   r  r  s   ``` r   dispatch_discord_editr    s;    |<;P;P;RP, $$%8%:L<M<MNr   boteditsrx   c           
      
  !K   ddl }t        |j                               !!syt        dt	        !       d|        	 !fd}t        j                  |       d{   }i }|D ]Z  }|d   }	t        |d         }
t        |d	         }|d
   }|
|f}||vrg ||<   ||   j                  |	|||	   d   ||	   d   d       \ |j                         D ]  \  \  }
}}	 | j                  |
      }|s	 | j                  |
       d{   }|s;	 |j                  |       d{   }|j$                  }d}g }|D ci c]  }|d   |
 }}|D ]  }d}|j&                  r*|j                         D ]  }	d|	 |j&                  v s|	} n |r||   }|d   }|d   }|d   }|j)                         }|j*                  xs d}d|v r|j-                  d      d   }n|}t        |j.                        }|j1                          |D ]  }|dk(  rB|j2                  dvs|j5                  |j2                  |j6                  |j8                         J|dk(  rB|j2                  dvs^|j5                  |j2                  |j6                  |j8                         |j5                  |j2                  |j6                  |j8                          |dk(  r8d|_        t        j<                  j?                  ddd      |_         d | |_        n|d!k(  rTd"|_        t        j<                  jC                         |_         t        |j                               }d#| d$| d%| |_        n|dk(  r|d&k(  r+d'|_        t        j<                  jE                         |_         nZ|d(k(  r+d)|_        t        j<                  jG                         |_         n*d*|_        t        j<                  jI                         |_         d+| |_        |j                  |       d,}|j                  |        |r:	 |jK                  |-       d{    |st        jL                  d.       d{     y7 # t        $ r}t        d|        Y d}~yd}~ww xY w7 # t        $ r}t        d|
 d|        Y d}~d}~ww xY w7 # t        j                  j                   $ r |D ]  }t#        |d   |
        Y t        $ r}t        d| d|
 d|        Y d}~9d}~ww xY wc c}w 7 7 # t        $ r}t        d/| d|        Y d}~d}~ww xY w# t        $ r} t        d0| d|         Y d} ~ d} ~ ww xY ww)1z9Execute the batched edits on Discord messages and SQLite.r   Nz([Discord Batch Edit] Starting flush for z products. Shutdown mode: c                  8   t               } 	 | j                         }dj                  d D              }|j                  d| d       |j	                         D cg c]  }t        |       c}| j                          S c c}w # | j                          w xY w)Nr   c              3       K   | ]  }d   yw)r  Nr   )r\  _s     r   r_  z0_perform_flush.<locals>.db_op.<locals>.<genexpr>   s     'C]]s   z
                    SELECT product_code, target_id as channel_id, message_id, event_type
                    FROM sent_messages
                    WHERE platform = 'discord' AND product_code IN (z)
                )r
   r;   r   r<   r   r   rA   )rC   r;   placeholdersr   product_codess       r   rE   z_perform_flush.<locals>.db_op  s    $&D
"xx'C]'CC $E FRN S  #	$
 *0):;):AQ):;

 <

s   AB B/B B Bz+[Discord Batch Edit] Database query error: r*  r    r!   r   r.   r  )r*  original_event_typer.   r  z-[Discord Batch Edit] Could not fetch channel r*   z,[Discord Batch Edit] Error fetching message r'   Fr  r  r3  r$  	cart_hold)r  r  r   	restocked)r  r  r  u#   ⚠️ Product Temporarily Reserved      u7   ⚠️ **Temporarily Reserved** • Reserved in cart.

sold_outr}  u:   🔴 **Temporarily Unavailable** • Confirmed sold at <t:z:t> (<t:z:R>).

r   r~  r   r  r|  u/   🆕 **Back in Stock** • Listing is active.

T)r  r  z+[Discord Batch Edit] Error editing message z.[Discord Batch Edit] Error processing message )'r  r  keysr   r  rH   rI   r1   r  r   rH  r  r  fetch_messagerS   r  NotFound_purge_discord_messager  r2   ry   r   r   fieldsclear_fieldsr   r   r   r   r   r   from_rgbr   rG  r   r  r   editr  )"r  r  rx   r  rE   r   r6   message_updatesrD   pcoder    r!   r  keyupdatesr  r;  updr  modified
new_embedsupdates_mapr  matched_coder.   r  new_embrP  
desc_cleanfields_to_processfield
sold_epochr  r  s"                                    @r   r}   r}     s    &M3C4F3GGabjak	lm	 &&u-- ON#\*+
\*+
!,/:&o%#%OC ##!#6El8,!%L8	%
 	 " .=-B-B-D) Z'e	Vooj1G$'$5$5j$AAG 	#11*== ZZFHJ @GGw3~.3wKG#77!,!1!1!3 =CGG3+0L! "4
  %l3C ]F#&~#6L*-.C*D'!hhjG"..4"D~%)ZZ%7%;
%)
 )-SZZ(8%((*!2![0$zz1`` ' 1 1uzz]b]i]i 1 j#{2$zz1rr ' 1 1uzz]b]i]i 1 j#--5::U[[Y^YeYe-f "3 ,(M(/(>(>sC(K0ijtiu.v+:-(M(/(9(9(;%(%5
0jkujvv~  @J  K  KT  U_  T`  /a+;..,>,CGM,3MM,>,>,@GM0L@,AGM,3MM,>,>,@GM,PGM,3MM,?,?,AGM0ablam.n+%%g.#H%%c*{ ~ Z((*(555#%mmC000C .E3 . :1#>?: B  HTVWXVYZ[ >>>** "C*3~+>
K # CJ<|\f[ggijkilmn HF 60  ZFzlRTUVTWXYYZ  	VA*RPRtTUU	Vsp  ;U+Q! QQ! !A;U+U1RRR
UU+R3"R0#R3'U;T:UBUAUGU!T6T7TTTU+Q! !	R*Q=8U+=RU+R	R-R("U&U+(R--U0R333T&U'U+)T1TU	U+TUTT	U %T;6U;U  U	U(U#U+#U((U+c                 J   	 t               }	 |j                         }|j                  d| t        |      f       |j	                          t        d|  d|        |j                          y # |j                          w xY w# t        $ r}t        d|        Y d }~y d }~ww xY w)Nz[DELETE FROM sent_messages WHERE platform = 'discord' AND product_code = ? AND target_id = ?z1[Discord Edit] Purged deleted message record for r'   z-[Discord Edit] Error purging message record: )r
   r;   r<   r=   r   r   rA   r1   )r*  r    rC   r;   r6   s        r   r  r    s    
B "	[[]FNNx  |H  JM  NX  JY  {Z  [KKMD\NR^_i^jklJJLDJJL B<QC@AABs/   
B AA, B ,A>>B 	B"
BB")F)
NNNNNr   NNNNr   )NN)ArH   typingr   r   r   r   backend.configr   backend.logging_configr   r	   backend.db.connectionr
   backend.scraper.enrichmentr   rS   r   r   ImportErrorr   __annotations__r   r   r   r   r   r+   r  r7   InteractionrM   rO   Clientr   commanddescribechoicesChoicer=   rh  Rolerb   rc   rd   re   rf   rg   rh   Userri   rj   rk   rl   rm   rn   r   rx  r  r  r  r  r  r  r}   r  r   r   r   <module>r*     s    , , # 5 3 :$K *.h~& -t WD WD Xn5M .1  w w wQU w4G,?,? D *w':': t 4Hw~~ 4Hn \{8lm\TNKELFTbRR \!MUZ[!GxX!AS!@P!5_M!>FXY!3<H!3<H	& 	 !%'+%)"&$($)26487;7;s]((s]s] e_s] E?	s]
 3-s] SMs] SMs] "',,/s] $GLL1s] 'w||4s] 'w||4s]	 n.s]j \}:rs_w/B/B _ t_6 \y6cdBd7+>+> Bd eBdH \<pqKj1D1D Kj rKjZ \x5_`T'*=*= T aT0 \~;ghT0C0C T iT, \x5rs['*=*= [ t[> \{  9C  D\ ?@bW-@-@ b b A Db4 \}:mn\ ABdw/B/B d',, d C od6 \0>stD73F3F D uD@ \w4rs\BK )-4j((4j4j uo4j	 t
4jl \y6^_\(TUh7+>+> hc h V `h> \{8tu?fW-@-@ ?f v?fJD4 Dd Dt DNe$ eC eHUO e_n eX "&"&RSRS RS 	RS
 3-RS 
RSp "&{O:{O{O {O 
	{OB "&9G9G9G 9G 
	9GvO OS OQT Oae O:XVn XVT#tCH~:M5N XVZ^ XVko XVtB B# B}+  Ks   S# #S.-S.