
    	i=              
          U d Z ddlZddlZddlZddlZddlZddlZddlmZm	Z	m
Z
 ddlmZ  e        ej                  j                  dd      Zej                  j                  dd      ZdZej                  j                  d	d      Zej                  j                  d
d      Zg dZddgZdddddddddd	Zeez   Zdad ZdedededefdZd=dededefdZej<                  j?                  d d!"      Z da!d#efd$Z"i Z#eeef   e$d%<   i a%eeef   e$d&<   d>d'e&e   d(e'deee&e   f   fd)Z(d*ed+ededz  fd,Z)d-edefd.Z*d/gd0gd1gd2gd3gd4gd5ged6Z+d=d7efd8Z,d9 Z-d: Z.d; Z/e0d<k(  r e/        yy)?u  
New Market Notifier — Telegram bot that monitors Kalshi for new esports
and political mentions markets. Sends alerts when new events appear and
supports /today to list upcoming events in the next 24 hours.

Uses the LuckySt BTC RSI bot telegram credentials.
Uses mom's Kalshi API credentials.

Efficient polling: only calls GET /events per series each cycle (9 calls).
Only calls GET /events/{ticker} when a NEW event is discovered.
All event details are cached for /today lookups.

Usage:
    python new_market.py
    N)datetimetimezone	timedelta)load_dotenvKALSHI_MOM_API_KEY KALSHI_MOM_API_SECRETz-https://api.elections.kalshi.com/trade-api/v2TELEGRAM_BOT_TOKENTELEGRAM_CHAT_ID)	KXCODGAME	KXLOLGAME	KXCS2GAMEKXVALORANTGAMEKXR6GAMEKXDOTA2GAMEKXOWGAMEKXTRUMPMENTIONBKXTRUMPMENTIONzCall of DutyzLeague of LegendsCS2ValorantzRainbow SixzDota 2	OverwatchzTrump Mentions)	r   r   r   r   r   r   r   r   r   c                  ^   t         } | sy 	 ddlm} ddlm} t
        j                  j                  |       rt        | d      j                         n| }|j                  t        |t              r|j                         n|d  |             S # t        $ r}t        d|        Y d }~y d }~ww xY w)Nr   )serialization)default_backendr)passwordbackendzKey load failed: )KALSHI_API_SECRETcryptography.hazmat.primitivesr   cryptography.hazmat.backendsr   ospathisfileopenreadload_pem_private_key
isinstancestrencode	Exceptionprint)secretr   r   key_dataes        "/home/npast/mentions/new_market.py_load_private_keyr0   S   s    F
@@/1ww~~f/E4$))+611!+Hc!:HOO?#4 2 
 	
  !!%&s   A?B 	B,B''B,	timestampmethodr"   returnc           	         t         syddlm} ddlm} |j                  d      d   }| |z   dz   |z   }t         j                  |j                         |j                  |j                  |j                               |j                  j                        |j                               }t        j                  |      j                         S )Nr   r   )hashes)padding?z/trade-api/v2)mgfsalt_length)_private_keyr   r5   )cryptography.hazmat.primitives.asymmetricr6   splitsignr)   PSSMGF1SHA256DIGEST_LENGTHbase64	b64encodedecode)r1   r2   r"   r5   r6   
path_cleanmsgsigs           r/   _sign_requestrH   d   s    5AC#J
f

.
;C




V]]_5!(!:!: 	 	<	C C ''))    endpointparamsc                 (   t        t        t        j                         dz              }t        |d|       }t        ||d}	 t        j                  t        | z   ||d      }|j                  dk(  r|j                         S i S # t        $ r i cY S w xY w)Ni  GET)zKALSHI-ACCESS-KEYzKALSHI-ACCESS-SIGNATUREzKALSHI-ACCESS-TIMESTAMP   )headersrK   timeout   )r(   inttimerH   KALSHI_API_KEYrequestsgetKALSHI_API_BASEstatus_codejsonr*   )rJ   rK   tsrG   rO   r   s         r/   
kalshi_getr[   t   s    	St#$	%B
E8
,C+#&#%G
LL83WV]_`==C668O	 	s   >B B BB   tg)max_workersthread_name_prefixtextc                 :      fd}t         j                  |       y )Nc                  t    	 t        j                  t         dt         ddd       y # t        $ r Y y w xY w)Nz/sendMessageHTML)chat_idr`   
parse_mode   )rY   rP   )rU   post_tg_base_urlr   r*   )r`   s   r/   _doztg_send.<locals>._do   s?    	MM.-!14vV
  		s   '+ 	77)_tg_poolsubmit)r`   ri   s   ` r/   tg_sendrl      s     OOCrI   event_cacheknown_tickersseries_liststaggerc                 P   i }t        |       D ]  \  }}|r|dkD  rt        j                  d       g }d}	 |ddd}|r||d<   t        d|      }|j	                  d	g       }	|j	                  d      }|	D ]#  }
|j                  |
j	                  d
d             % |r|	snj|||<    |S )zCheap scan: only GET /events per series, return {series: [event_tickers]}.
    Staggers 1 request per second across series to spread API load.r      Nr$   rQ   )series_tickerstatuslimitcursorz/eventseventsevent_tickerr   )	enumeraterS   sleepr[   rV   append)ro   rp   resultiseriestickersrv   rK   datarw   events              r/   scan_event_tickersr      s     F{+ !	6q1uJJqM'-#NF#)x i0DXXh+FXXh'F >uyy<=>   !v%!& MrI   rx   r~   c           
      .   t        d|        }|sy|j                  d|      }|j                  d|       }|j                  dg       }|sy|d   }|j                  d      xs |j                  dd	      }|sy	 t        j                  |j	                  d
d            }|}	|D 
cg c]  }
|
j                  dd	       }}
|t        v rdnd}t        d |D              }|t        j                  ||      || ||	|||d	S # t
        t        f$ r Y yw xY wc c}
w )zFFetch full event detail (markets, timing). Only called for NEW events.z/events/Nr   titlemarketsr   expected_expiration_time
close_timer   Zz+00:00tickermentionsesportsc              3   R   K   | ]  }t        |j                  d d             ! yw)volumer   N)rR   rV   ).0m2s     r/   	<genexpr>z%fetch_event_detail.<locals>.<genexpr>   s      BBs266(A./Bs   %')	r~   series_namecategoryrx   r   
event_timeevent_time_utcr   r   )
r[   rV   r   fromisoformatreplace
ValueError	TypeErrorMENTIONS_SERIESsumSERIES_NAMES)rx   r~   	ev_detailr   r   r   mexp_strexp_dtexp_etr   market_tickersr   total_volumes                 r/   fetch_event_detailr      s3   Xl^45IMM'9-EIIg|,EmmIr*G
Aee./J155r3JG''X(FG F5<=rbffXr*=N=#6zIH B'BBL #''7$ !
 
 	"  >s   9%C= $D=DDevc                    | d   j                  d      }| d   j                  d      }dj                  d | d   dd D              }| d	   d
k(  rdnd}| j                  dd      }|rd|dnd}| d| d    d| d| d| d    d| d| S )z+Format a single event for telegram display.r   z%I:%M %p ETz%m/%d
c              3   (   K   | ]
  }d | d  yw)z  <code>z</code>N )r   r   s     r/   r   zformat_event.<locals>.<genexpr>   s     Mahqc1Ms   r   N   r   r   u   🎮u   📢r   r   $,$0z <b>r   u
   </b>
📅 z at u   
🏷 r   u   
📊 Volume: z

Markets:
)strftimejoinrV   )r   tdmarkets_strtagvolvol_strs          r/   format_eventr      s    
<!!-0A
<!!'*A))M2i=!;LMMKzNi/&VC
&&1
C #akdG%tB}%& 's$qc 7} y ! M		#rI   r   r   r   r   r   r   r   )lolcscodvaloowdotar6r   filter_namec                 <   t        j                  t        j                        }|t	        d      z   }| r+| t
        v r#t        t
        |          }| j                         }nd}d}t        d| d       t        t        j                               D ]&  \  }}|t        vst        ||      }|s|t        |<   ( t        j                         D cg c]  }||d   cxk  r|k  rn n||d   |v r|  }	}|	j                  d	 
       |	st        d| d       y|	D cg c]  }|d   dk(  s| }
}|	D cg c]  }|d   dk(  s| }}d| dt!        |	       dg}|
r|j#                  dt!        |
       d       |
D ]V  }|d   j%                  d      }|j'                  dd      }|rd|dnd}|j#                  d| d|d    d|d     d!| d"	       X |j#                  d#       |r|j#                  d$t!        |       d       |D ]V  }|d   j%                  d      }|j'                  dd      }|rd|dnd}|j#                  d| d|d    d|d     d!| d"	       X |j#                  d#       t        d%j)                  |             yc c}w c c}w c c}w )&zoSend list of events in next 24 hours, optionally filtered.
    Fetches details for any events not yet in cache.   )hoursNAllu   🔍 Scanning events (z)...r   r~   c                     | d   S )Nr   r   )r.   s    r/   <lambda>zhandle_today.<locals>.<lambda>'  s
    , rI   )keyu   📭 No z events in the next 24 hours.r   r   r   u"   📋 <b>Events — Next 24 Hours (z)</b> (z total)
u   ━━ 🎮 <b>ESPORTS (u   )</b> ━━r   z%I:%M %pr   r   r   r   r   z  u    — r   z: r   z | z volr   u   ━━ 📢 <b>MENTIONS (r   )r   nowr   utcr   TODAY_FILTERSsetupperrl   listrn   itemsrm   r   valuessortlenr{   r   rV   r   )r   now_utc
cutoff_utcallowed_serieslabelr   r~   detailr.   upcomingr   r   linesr   r   r   r   s                    r/   handle_todayr     s    ll8<<(G92..J {m3];78!!#$UG401 }2245 -$'7F&,F#	- %%'a()7Z7#q{n'D 	
H 
 MM/M0(5'!>?@"AQa
my&@qAGA#Caq}
'BCHC1%HiXYE/G~\JK 	ZB< ))*5A&&1%C%(#akdGLL2aSb&7%82g;-s7)SWXY		Z
 	R0X|LM 	ZB< ))*5A&&1%C%(#akdGLL2aSb&7%82g;-s7)SWXY		Z
 	RDIIeE BCs   #JJ+J5JJc                     t        d       t        t              } d}d}| j                         D ](  \  }}|D ]  }|t        |<   |t
        v r|dz  }|dz  }  * t        t              }t        d| d| d| d       t        dt        t               d	t        t
               d
| d| d| dt        t               d       	 	 t        t              } i }| j                         D ]e  \  }}|D ][  }|||<   |t        vs|t        |<   t        ||      }|s)|t        |<   dt        |       }	t        |	       t        d|d           ] g t        t        j                               t        |j                               z
  }
|
D ].  }t        j                  |d       t        j                  |d       0 	 # t        $ r}t        d|        Y d}~d}~ww xY w)zhMain loop: poll series for event tickers (cheap), only fetch
    details when a new event is discovered.zInitial scan...r   rr   zFound z	 events (z
 esports, z
 mentions)u/   🟢 <b>Market Notifier Started</b>
Monitoring z esports + z mentions series
Tracking z* mentions)
Continuous scan: 1 series/sec (s per cycle)u   🆕 <b>NEW MARKET</b>

zNew: r   NzMonitor error: )r+   r   
ALL_SERIESr   rn   r   r   rl   ESPORTS_SERIESr   rm   r   r   keyspopr*   )all_tickersesports_countmentions_countr~   r   r   
init_countcurrent_allr   rF   expiredr.   s               r/   monitor_new_marketsr   K  s   
 

$Z0KMN&,,. # 	#F$*M&!(!#"	## ]#J	F:,ij@PPZ
[\.)*+c/6J5K L<yz.AQ R**-j/):,	H 	),Z8K*,K#.#4#4#6 =% =F*0K']206f-!3FF!C!28K/$>|F?S>T"UC#CL!E&/):";<== -,,./#k6F6F6H2IIG! .!!&$/-.- 4  	)OA3'((	)s%   9F: ?F: B!F: :	GGGc                  r    dt         fd} t        j                  | d      }|j                          y)z1Listen for /today and /help commands on telegram.r   c            
         	 	 t        j                   ddz   ddd      } | j                  dk(  r| j                         j                  dg       D ]  }|d	   |j                  d
i       }|j                  dd      j	                         j                         }|dk(  rt                X|j                  d      r\|j                  dd      d   }|t        v rt        |       t        d| ddj                  t        j                                       |dk(  sdj                  d t        D              }t        d| d        L# t        $ r t        j                  d       Y "w xY w)Nz/getUpdatesrr   
   )offsetrP   rN   )rK   rP   rQ   r|   	update_idmessager`   r   z/today/today_zUnknown filter: z
Available: , z/helpc              3   &   K   | ]	  }d |   yw)r   Nr   )r   fs     r/   r   z7start_command_listener.<locals>.poll.<locals>.<genexpr>  s     /U!'!/Us   u<   📖 <b>Commands</b>
/today — All events in next 24 hours
u   
/help — Show this messagerf   )rU   rV   rX   rY   striplowerr   
startswithr<   r   rl   r   r   r*   rS   rz   )respupdaterF   r`   r   filtersbase_urllast_update_ids         r/   pollz$start_command_listener.<locals>.poll  sc   ||j,&4q&8RH
 ##s*"&))+//(B"? )/)<$jjB7"wwvr288:@@B8+(N!__Y7*.**Y*B1*EK*m; ,[ 9 '*:;-}UYU^U^_l_q_q_sUtTu(v w!W_&*ii/U}/U&UG#!K#*) ,>!> 8  

1s   DE  -E E0/E0T)targetdaemonN)rh   	threadingThreadstart)r   r   r   r   s     @@r/   start_command_listenerr     s0    NHB 	T2AGGIrI   c                     t               adt         at        rt        st        d       y t        st        d       y t        dd        t        d       t        d        t        ddj                  t                      t        d	dj                  t                      t        d
t        t               d       t        d       t        d d       t                t                y )Nzhttps://api.telegram.org/botz5Missing TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID in .envz:Missing KALSHI_MOM_API_KEY / KALSHI_MOM_API_SECRET in .envr   z2==================================================z  NEW MARKET NOTIFIERz  Esports:  r   z  Mentions: z&  Scan:     continuous, 1 series/sec (r   z  Telegram: BTC RSI bot)r0   r:   r
   rh   r   r+   r   r   r   r   r   r   r   r   rI   r/   mainr     s     %&L12D1EFL%5EFJK	Bvh-	!#	VH	L>23
45	L?34
56	23z?2C<
PQ	#%	VHB-rI   __main__)N)T)1__doc__rB   r!   rS   rU   r   concurrent.futures
concurrentr   r   r   dotenvr   environrV   rT   r   rW   r
   r   r   r   r   r   r:   r0   r(   rH   dictr[   futuresThreadPoolExecutorrj   rh   rl   rm   __annotations__rn   r   boolr   r   r   r   r   r   r   r   __name__r   rI   r/   <module>r     s0     	     2 2    4b9JJNN#:B? A ZZ^^$8"= ::>>"4b9    $ '&
 o-
 "*S *# *S *S *  d d * 00QSW0X
# 
$  "T#t)_ ! "tCH~ "DI  SRVWZR[^H\ 4(S (# ($+ (VT c . !"	9c 9@6)z'\4 zF rI   