
C –jØ¤  ã               @   s™  d  d l  Z  d  d l Z d  d l Z d  d l Z d  d l Z d  d l Z d  d l m Z d  d l m Z d d „  Z	 e	 ƒ  y d  d l
 Z
 Wn e k
 r¥ d Z
 Yn Xy d  d l Z Wn e k
 rÐ d Z Yn Xd Z e  j d Z e  j d Z e  j d	 Z e e  j d
 ƒ Z e e  j d ƒ Z e e g Z d Z d Z d Z d Z d Z d~ Z e j d e j d d d e j d ƒ e j ƒ  g ƒ e j  d ƒ Z! d j" e ƒ Z# d d „  Z$ d d d „ Z% d d d „ Z& d  d! „  Z' e e h Z( d" d# „  Z) e e* ƒ Z+ e j, ƒ  Z- d$ d i Z. d' d( „  Z/ d) d* „  Z0 d d+ d, „ Z1 d- d. „  Z2 g  a3 d  a4 d/ a5 d0 d1 „  Z6 d2 d3 „  Z7 d4 d5 „  Z8 d€ Z9 d9 d: „  Z: d; d< „  Z; d= d> „  Z< d? d@ „  Z= dA dB „  Z> dC dD „  Z? dE dF „  Z@ dG dH „  ZA dI dJ „  ZB dK dL „  ZC dM dN „  ZD dO dP „  ZE dQ dR dS „ ZF dT dU „  ZG dV dW „  ZH d dX dY „ ZI dZ d[ „  ZJ d\ d] „  ZK d^ d_ „  ZL d` da „  ZM db dc „  ZN dd de „  ZO df dg „  ZP dh di „  ZQ dj dk „  ZR dl dm „  ZS dn do „  ZT dp dq „  ZU dr ds „  ZV dt du „  ZW dv dw „  ZX eY dx k r•e! jZ dy e e ƒ e7 ƒ  e j[ dz eM d{ d ƒ j\ ƒ  e r�e j] ƒ  r�e j[ dz eX d{ d ƒ j\ ƒ  e! jZ d| ƒ n e! jZ d} ƒ eU ƒ  d S)�é    N)Údatetime)Údefaultdictc              C   së   t  j j t  j j t  j j t ƒ ƒ d ƒ }  y¥ t |  ƒ �’ } xˆ | D]€ } | j ƒ  } | sF | j d ƒ sF d | k r} qF | j	 d d ƒ \ } } t  j
 j | j ƒ  | j ƒ  j d ƒ j d ƒ ƒ qF WWd  QRXWn t k
 ræ Yn Xd  S)Nz.envú#ú=é   ú"ú')ÚosÚpathÚjoinÚdirnameÚabspathÚ__file__ÚopenÚstripÚ
startswithÚsplitÚenvironÚ
setdefaultÚIOError)ZrutaÚfÚlineaÚkÚv© r   úbot.pyÚ_cargar_dotenv   s    -"@r   Z__mattermost__ÚSMARTOLT_BASEZSMARTOLT_API_KEYZTELEGRAM_BOT_TOKENZTELEGRAM_CHAT_IDZTELEGRAM_GROUP_IDzonu_state.jsonzonu_individual_state.jsoné   Té   é   é<   ÚlevelÚformatz'%(asctime)s [%(levelname)s] %(message)sZhandlerszbot.logZsmartoltzhttps://api.telegram.org/bot{}c             C   sr   y/ t  j d j t |  ƒ d | d d ƒj ƒ  SWn< t k
 rm } z t j d |  | ƒ i  SWYd  d  } ~ Xn Xd  S)Nz{}/{}ÚjsonÚtimeouté
   ztelegram %s: %s)ÚrequestsZpostr#   ÚTELEGRAMr$   Ú	ExceptionÚlogÚerror)ÚmethodÚdataÚer   r   r   Útg]   s
    /r/   c             C   s‹   |  t  k r# t r t j | ƒ d  Sd |  d | d  d … d d d d i } | r[ | | d <t d	 | ƒ t r‡ |  t k r‡ t j | ƒ d  S)
NÚchat_idÚtexti   Ú
parse_modeÚMarkdownZprotect_contentTÚreply_markupZsendMessage)ÚMM_CHATÚ
mattermostÚpublicarr/   ÚALERT_TARGETS)Úchatr1   r4   Úpr   r   r   Úsendd   s    
r;   c             C   sl   |  t  k r# t r t j | ƒ d  Sd |  d | d | d  d … d d i } | r[ | | d <t d | ƒ d  S)	Nr0   Ú
message_idr1   i   r2   r3   r4   ZeditMessageText)r5   r6   r7   r/   )r9   Úmsg_idr1   r4   r:   r   r   r   Úeditx   s    (
r>   c             C   s   t  d d |  i ƒ d  S)NZanswerCallbackQueryZcallback_query_id)r/   )Zcb_idr   r   r   Úanswer…   s    r?   c             C   s$   |  t  k r d St j d |  ƒ d S)zASolo el chat privado del admin y el grupo NOC pueden usar el bot.TzAcceso denegado desde chat %sF)ÚALLOWED_CHATSr*   Úwarning)r9   r   r   r   Ú
autorizadoŠ   s    rB   zonu/get_all_onus_detailsé   é  c                s    |  t  k r d St  |  \ } ‰ t j ƒ  ‰  t �g ‡  ‡ f d d †  t |  Dƒ t |  <t t |  ƒ | k r‘ t j d |  t t |  ƒ | ƒ d Sd SWd  QRXd  S)NTc                s&   g  |  ] } ˆ  | ˆ k  r | ‘ q Sr   r   )Ú.0Út)ÚahoraÚventanar   r   ú
<listcomp>Ÿ   s   	 z!_puede_llamar.<locals>.<listcomp>u   â›” LÃ­mite API %s (%d/%d)F)Ú
API_LIMITSÚtimeÚ	_api_lockÚ
_api_callsÚlenr*   rA   )ÚendpointZ	max_callsr   )rG   rH   r   Ú_puede_llamar™   s    $ rP   c          	   C   s)   t  � t |  j t j ƒ  ƒ Wd  QRXd  S)N)rL   rM   ÚappendrK   )rO   r   r   r   Ú
_registrar¥   s    rR   c             C   sž   t  |  ƒ s d  SyK t j d j t |  ƒ d d t i d | d d ƒ} t |  ƒ | j ƒ  SWn< t k
 r™ } z t	 j
 d |  | ƒ d  SWYd  d  } ~ Xn Xd  S)Nz{}/{}ZheaderszX-TokenÚparamsr%   r   z
api %s: %s)rP   r'   Úgetr#   r   ÚAPI_KEYrR   r$   r)   r*   r+   )rO   rS   Úrr.   r   r   r   Úapi©   s    
rW   c                  s?   t  j  ƒ  ‰  t �& t ‡  f d d †  t d Dƒ ƒ SWd  QRXd  S)Nc                s&   g  |  ] } ˆ  | d  k  r | ‘ q S)i  r   )rE   rF   )rG   r   r   rI   »   s   	 z calls_usadas.<locals>.<listcomp>zonu/get_all_onus_details)rK   rL   rN   rM   r   r   )rG   r   Úcalls_usadas¸   s    rX   Fc               C   s   t  S)N)Ú_cache_onusr   r   r   r   Úget_onus_cachedÅ   s    rZ   c              C   s¹   t  d ƒ }  |  d  k	 rD d |  k rD t j d t |  ƒ d  d … ƒ |  rµ d |  k rµ |  d a t j ƒ  a t j d t t ƒ ƒ t	 rµ t rµ t j d t
 t d j ƒ  ƒ ƒ d a	 t S)	Nzonu/get_all_onus_detailsÚonusz4API respondio sin 'onus' (posible limite remoto): %séÈ   u   CachÃ© actualizado: %d ONUsz$DEBUG campos disponibles por ONU: %sr   T)rW   r*   rA   ÚstrrY   rK   Ú_cache_timeÚinforN   Ú_potencia_debug_loggedÚsortedÚkeys)r-   r   r   r   Úget_onus_freshÈ   s     
 rc   c               C   s   t  r t t j ƒ  t  ƒ Sd S)Ni'  )r^   ÚintrK   r   r   r   r   Ú
edad_cacheÖ   s    re   Úsignal_1310Úsignal_1490Úsignalc             C   s4   x- t  D]% } |  j | ƒ } | d k r | Sq Wd S)NÚ úN/Aú-)Nri   rj   rk   )ÚCAMPOS_POTENCIArT   )ÚoZcampor   r   r   r   Úget_potenciaâ   s
    rn   c             C   sŽ   |  d k r t  |  ƒ j ƒ  n d } | j ƒ  j d ƒ rO | d d	 … j ƒ  } | d
 k r_ d Sy t | ƒ Wn t k
 r… | SYn X| d S)aN  Potencia lista para mostrar, con la unidad puesta una sola vez.

    La API ya devuelve el valor con unidad dentro ("-20.05 dBm"), asi que se
    quita y se vuelve a poner aqui; antes salia "-20.05 dBm dBm".
    Ojo con el campo 'signal', que es cualitativo ("Very good") y no una
    medida: en ese caso se muestra tal cual, sin dBm.Nri   úN/AZdbmé   rk   z dBm)Nri   éýÿÿÿ)ri   rk   ro   )r]   r   ÚlowerÚendswithÚfloatÚ
ValueError)r   Ztxtr   r   r   Úpotencia_txté   s    $	rv   c             C   se   |  j  d ƒ p d j ƒ  } | j ƒ  d k r7 t |  ƒ St | j  t |  ƒ ƒ ƒ } | j  d ƒ pd d S)u»   Potencia a mostrar: en vivo si estÃ¡ online; Ãºltima registrada (guardada
    la Ãºltima vez que estuvo online) si estÃ¡ en LOS/offline, ya que en LOS
    la lectura en vivo no es fiable.Ústatusri   ÚonlineÚultima_potenciazN/A)rT   r   rr   rn   Ú_normalizar_prevÚ	clave_onu)rm   Ú
estado_onurw   Úprevr   r   r   Úget_potencia_mostrarû   s
    
r~   c              C   sD   y' t  t ƒ � }  t j |  ƒ SWd  QRXWn t k
 r? i  SYn Xd  S)N)r   Ú
STATE_FILEr$   Úloadr)   )r   r   r   r   Úcargar_estado	  s
    r�   c             C   si   y- t  t d ƒ � } t j |  | ƒ Wd  QRXWn5 t k
 rd } z t j d | ƒ WYd  d  } ~ Xn Xd  S)NÚwzguardar_estado: %s)r   r   r$   Údumpr)   r*   r+   )r.   r   Úexr   r   r   Úguardar_estado  s
    r…   c              C   sD   y' t  t ƒ � }  t j |  ƒ SWd  QRXWn t k
 r? i  SYn Xd  S)N)r   ÚONU_STATE_FILEr$   r€   r)   )r   r   r   r   Úcargar_estado_onus  s
    r‡   c             C   si   y- t  t d ƒ � } t j |  | ƒ Wd  QRXWn5 t k
 rd } z t j d | ƒ WYd  d  } ~ Xn Xd  S)Nr‚   zguardar_estado_onus: %s)r   r†   r$   rƒ   r)   r*   r+   )r.   r   r„   r   r   r   Úguardar_estado_onus"  s
    rˆ   c             C   s+   |  j  d ƒ p* |  j  d ƒ p* |  j  d ƒ S)zFIdentificador estable de la ONU: SN si existe, si no id, si no nombre.ZsnÚidÚname)rT   )rm   r   r   r   r{   )  s    r{   c             C   s)   t  |  t ƒ r d |  d d i S|  p( i  S)zDCompatibilidad con el formato antiguo del state file (string plano).rw   Ú	los_desdeN)Ú
isinstancer]   )r}   r   r   r   rz   -  s    rz   c             C   s¨  i  } t  ƒ  } t j ƒ  } x€|  D]x} t | ƒ } | s= q" | j d ƒ pO d j ƒ  } | j ƒ  d k rs d } n! | j ƒ  d k rŽ d } n d } t | j | ƒ ƒ }	 | d k rø |	 j d ƒ d k ræ |	 j d ƒ ræ |	 d }
 qþ | j	 d ƒ }
 n d	 }
 | d k rt
 | ƒ } n |	 j d
 ƒ } d | d |
 d
 | d |	 j d d ƒ i | | <| d k r" |
 r" | t j |
 d ƒ j } | t k r" | j | ƒ q" W| | f S)uÅ   
    Recorre todas las ONUs y devuelve:
      estado_actual: dict key -> {status, los_desde, ultima_potencia}
      cronicos: set de keys en LOS ininterrumpido >= UMBRAL_LOS_ANTIGUA_DIAS dÃ­as
    rw   ri   ÚLOSÚlosrx   Úoffliner‹   z%Y-%m-%d %H:%M:%SNry   Úrecuperacion_pendienteF)Úsetr   Únowr{   rT   r   Úupperrr   rz   Ústrftimern   ÚstrptimeÚdaysÚUMBRAL_LOS_ANTIGUA_DIASÚadd)r[   Úestado_anteriorÚestado_actualÚcronicosrG   rm   Úkeyrw   Ústatus_normr}   r‹   ry   Údiasr   r   r   Úactualizar_estado_los3  s>    			$rŸ   c             C   s­  t  d d „  ƒ } x”|  D]Œ} | j d d ƒ } t | j d d ƒ ƒ } t | j d d ƒ ƒ } | s | d k s | d k rƒ q | j d d ƒ j ƒ  } d j | | | ƒ } | | | d	 <| | | d <| | | d <| j d
 d ƒ | | d <| | d d 7<| j ƒ  d k rB| | d d 7<| | d j | ƒ q | j ƒ  d k r~| | d d 7<| | d j | ƒ q | | d d 7<| | d j | ƒ q W| S)Nc               S   sF   d d d d d d d d d d d d d	 d d
 d d g  d g  d g  i S)NÚoltri   ÚboardÚportÚzonaÚtotalr   rx   rŽ   r�   Úonus_losÚonus_offlineÚonus_onliner   r   r   r   r   Ú<lambda>m  s    zanalizar_pons.<locals>.<lambda>Úolt_nameri   r¡   r¢   rw   z{}/{}/{}r    Ú	zone_namer£   r¤   r   r�   rŽ   r¥   rx   r§   r�   r¦   )r   rT   r]   r   r#   r“   rQ   rr   )r[   Úponsrm   r    r¡   r¢   rw   rœ   r   r   r   Úanalizar_ponsl  s.    r¬   c             C   s2   |  d |  d } | r. t  |  d d | ƒ Sd S)zG% de LOS sobre (online + LOS), es decir, sobre las que estaban activas.rx   rŽ   éd   r   )Úround)ÚstatsÚbaser   r   r   Úcalcular_pct_los“  s    r±   u   CAÃ�DA DE CAJA DETECTADAc             C   s¯  |  d } |  d } |  d } |  d } |  d } |  d } |  d } |  d }	 t  |  ƒ }
 d	 j | ƒ } | d
 j | ƒ 7} | d j | | ƒ 7} | d j | ƒ 7} | d j |	 ƒ 7} | d j | |
 ƒ 7} | d j | ƒ 7} | d j | ƒ 7} t ƒ  } |  d r | d 7} xŠ |  d D]~ } | j d d ƒ } | j d d ƒ } t | | ƒ } | d j | ƒ 7} | rƒ| d j | ƒ 7} | d j t | ƒ ƒ 7} qW|  d r#| d 7} xl |  d D]` } | j d d ƒ } | j d d ƒ } | d j | ƒ 7} | r| d j | ƒ 7} | d 7} q¿W|  d r‰| d 7} xO |  d D]C } | j d d ƒ } t | | ƒ } | d  j | t | ƒ ƒ 7} qBW| d! j t j ƒ  j d" ƒ ƒ 7} | S)#Nr    r¡   r¢   r£   r¤   rŽ   r�   rx   u	   ðŸš¨ {}

u   ðŸ“¡ OLT: `{}`
u   ðŸ“¦ Board `{}` / PON `{}`
u   ðŸ“� Zona: {}

u   ðŸŸ¢ Online: {}
u$   âš ï¸� LOS: {} ({}% de las activas)
u   ðŸ”´ Offline: {}
zTotal PON: {}

r¥   u   âš ï¸� ONUs en LOS:
rŠ   z
Sin nombreÚaddressri   u	   âš ï¸� {}u    â€” {}u    â€” ðŸ”‹ `{}`
r¦   u   
ðŸ”´ ONUs Offline:
u   ðŸ”´ {}Ú
r§   u   
ðŸŸ¢ ONUs Online:
u   ðŸŸ¢ {} â€” ðŸ”‹ `{}`
u	   
â�± `{}`z%Y-%m-%d %H:%M:%S)	r±   r#   r‡   rT   r~   rv   r   r’   r”   )r¯   Útipor    r¡   r¢   r£   r¤   Zn_losZn_offZn_onÚpct_losÚmsgr|   rm   ÚnombreÚaddrÚpotenciar   r   r   Úformato_alertaœ  sZ    







	





 "rº   c             C   sÄ   d } | d j  |  d ƒ 7} | d j  |  d |  d ƒ 7} | d j  |  d ƒ 7} | d	 j  |  d
 |  d ƒ 7} | d j  |  d ƒ 7} | d j  |  d ƒ 7} | d j  t j ƒ  j d ƒ ƒ 7} | S)Nu   âœ… CAJA RECUPERADA

u   ðŸ“¡ OLT: `{}`
r    u   ðŸ“¦ Board `{}` / PON `{}`
r¡   r¢   u   ðŸ“� Zona: {}

r£   u   ðŸŸ¢ Online: {}/{}
rx   r¤   u   âš ï¸� LOS: {}
rŽ   u   ðŸ”´ Offline: {}

r�   u   â�± `{}`z%Y-%m-%d %H:%M:%S)r#   r   r’   r”   )r¯   r¶   r   r   r   Úformato_recuperacionÒ  s    "r»   c             C   s	  |  j  d d ƒ } |  j  d d ƒ } |  j  d d ƒ } |  j  d d ƒ } |  j  d d ƒ } t |  ƒ } d } | d	 j | ƒ 7} t r˜ | t j | ƒ 7} | d
 j | | | ƒ 7} | rÊ | d j | ƒ 7} | d j t | ƒ ƒ 7} | d j t j ƒ  j d ƒ ƒ 7} | S)NrŠ   z
Sin nombrer©   ri   r¡   r¢   r²   u   ðŸ†• ALTA REALIZADA

u   ðŸ‘¤ Cliente: {}
u)   ðŸ“¡ OLT: `{}` â€” Board `{}` / PON `{}`
u   ðŸ“� {}
u   ðŸ”‹ Potencia: `{}`
u   â�± `{}`z%Y-%m-%d %H:%M:%S)	rT   rn   r#   Ú	telefonosr   rv   r   r’   r”   )rm   r·   r    r¡   r¢   r¸   r¹   r¶   r   r   r   Úformato_alta_onuá  s     "r½   c             C   sý   |  j  d d ƒ } |  j  d d ƒ } |  j  d d ƒ } |  j  d d ƒ } |  j  d d ƒ } d } | d	 j | ƒ 7} t rŒ | t j | ƒ 7} | d
 j | | | ƒ 7} | r¾ | d j | ƒ 7} | d j t | ƒ ƒ 7} | d j t j ƒ  j d ƒ ƒ 7} | S)NrŠ   z
Sin nombrer©   ri   r¡   r¢   r²   u   âš ï¸� ONT EN LOS

u   ðŸ‘¤ Cliente: {}
u)   ðŸ“¡ OLT: `{}` â€” Board `{}` / PON `{}`
u   ðŸ“� {}
u&   ðŸ”‹ Ultima potencia registrada: `{}`
u   â�± `{}`z%Y-%m-%d %H:%M:%S)rT   r#   r¼   r   rv   r   r’   r”   )rm   ry   r·   r    r¡   r¢   r¸   r¶   r   r   r   Úformato_los_onuõ  s    "r¾   c             C   sÞ   |  j  d d ƒ } |  j  d d ƒ } |  j  d d ƒ } |  j  d d ƒ } t |  ƒ } d } | d j | ƒ 7} t r† | t j | ƒ 7} | d	 j | | | ƒ 7} | d
 j t | ƒ ƒ 7} | d j t j ƒ  j d ƒ ƒ 7} | S)NrŠ   z
Sin nombrer©   ri   r¡   r¢   u   âœ… RECUPERADA

u   ðŸ‘¤ Cliente: {}
u)   ðŸ“¡ OLT: `{}` â€” Board `{}` / PON `{}`
u%   ðŸ”‹ Potencia de recuperaciÃ³n: `{}`
u   â�± Hora de recuperaciÃ³n: `{}`z%Y-%m-%d %H:%M:%S)	rT   rn   r#   r¼   r   rv   r   r’   r”   )rm   r·   r    r¡   r¢   r¹   r¶   r   r   r   Úformato_recuperada_onu  s    "r¿   c             C   s{  t  | ƒ d k } xb|  D]Z} t | ƒ } | s4 q t | j | ƒ ƒ } | j d ƒ } | j d d ƒ }	 | | d }
 | j d d ƒ } d j | j d d	 ƒ | j d
 d	 ƒ | j d d	 ƒ ƒ } | | k r"| r |
 d k r t | ƒ } x t D] } t | | ƒ qõ Wt j	 d | | ƒ q |
 d k rÀ| d k rÀd | | d <| | k rpt
 rpt j	 d | | ƒ qst | | | j d ƒ ƒ } x t D] } t | | ƒ q“Wt j	 d | | ƒ q |
 d k r)|	 r)d | | d <t | ƒ } x t D] } t | | ƒ qóWt j	 d | | t | ƒ ƒ q |
 d k re| d k red | | d <t j	 d | | ƒ q d | | d <q Wd S)u@  
    Compara el estado de cada ONU frente al chequeo anterior:
      - Aparece por primera vez y estÃ¡ online  â†’ ALTA REALIZADA (tras el arranque inicial)
      - Pasa de online/offline a LOS           â†’ ONT EN LOS
      - Pasa de LOS a online                   â†’ NO avisa aÃºn, marca recuperacion_pendiente
                                                  (la potencia reciÃ©n recuperada no es fiable
                                                  hasta el siguiente chequeo de la API)
      - Sigue online con recuperacion_pendiente â†’ RECUPERADA (potencia ya fresca, confirma)
    `onus` ya viene filtrada sin las ONTs crÃ³nicas en LOS (ver UMBRAL_LOS_ANTIGUA_DIAS).
    `pons_en_caida` es el set de claves olt/board/port en alerta de caja, para no
    duplicar aviso individual por cada ONT de una caÃ­da masiva.
    r   rw   r�   FrŠ   z
Sin nombrez{}/{}/{}r©   ri   r¡   r¢   rx   u   ðŸ†• Alta: %s (%s)rŽ   u8   âš ï¸� LOS individual omitido (caja %s ya en alerta): %sry   u   âš ï¸� LOS individual: %s (%s)u0   âœ… Recuperada (confirmada): %s (%s) potencia=%sTuM   â�³ RecuperaciÃ³n pendiente de confirmar (esperando potencia fresca): %s (%s)N)rN   r{   rz   rT   r#   r½   r8   r;   r*   r_   ÚSUPRIMIR_LOS_INDIVIDUAL_SI_CAJAr¾   r¿   rn   )r[   Úpons_en_caidar™   rš   Z	bootstraprm   rœ   r}   Zprev_statusZprev_pendienter�   r·   Zpon_keyr¶   Útargetr   r   r   Úcheck_onus_individual  sH    9rÃ   c                 s  t  j d t ƒ  ƒ t ƒ  }  |  s& d  St ƒ  } t |  | ƒ \ } ‰  ˆ  rc t  j d t t ˆ  ƒ ƒ ‡  f d d †  |  Dƒ } t ƒ  } i  } t	 | ƒ } t
 ƒ  } x0| j ƒ  D]"\ } }	 t |	 ƒ }
 |	 d d k oÞ |
 t k } | j | d ƒ } | | | <| r| j | ƒ | rv| rvt |	 ƒ } x t D] } t | | ƒ q0Wt  j d |	 d	 |	 d
 |	 d |	 d |
 ƒ q­ | r­ | r­ t |	 ƒ } x t D] } t | | ƒ q–Wt  j d |	 d	 |	 d
 |	 d ƒ q­ Wt | ƒ t | | | | ƒ t | ƒ t  j d t | ƒ ƒ d  S)Nu%   ðŸ”� Chequeo (llamadas usadas: %d/15)u8   ðŸ’€ ONTs crÃ³nicas en LOS (â‰¥%d dÃ­as, sin avisar): %dc                s(   g  |  ] } t  | ƒ ˆ  k r | ‘ q Sr   )r{   )rE   rm   )r›   r   r   rI   p  s   	 zcheck_onus.<locals>.<listcomp>rŽ   r   Fu3   ðŸš¨ Alerta %s B%s/P%s â€” %d LOS (%d%% de activas)r    r¡   r¢   u   âœ… Recuperada %s B%s/P%su#   âœ… Chequeo OK. PONs analizados: %d)r*   r_   rX   rc   r‡   rŸ   r—   rN   r�   r¬   r‘   Úitemsr±   ÚLOS_PCT_UMBRALrT   r˜   rº   r8   r;   r»   r…   rÃ   rˆ   )r[   Zestado_onu_anteriorZestado_onu_actualZonus_para_alertarr™   rš   r«   rÁ   rœ   r¯   rµ   Zes_caidaZya_alertador¶   rÂ   r   )r›   r   Ú
check_onusa  sF    				
	&&

rÆ   c              C   sh   t  j d ƒ xT y t ƒ  Wn5 t k
 rR }  z t j d |  ƒ WYd  d  }  ~  Xn Xt  j t ƒ q Wd  S)Né   zloop_monitor: %s)rK   ÚsleeprÆ   r)   r*   r+   ÚMONITOR_INTERVAL)r.   r   r   r   Úloop_monitor˜  s    #rÊ   c             C   s'   t  |  d j t t t ƒ  d ƒ ƒ d  S)Nu  ðŸ¤– SmartOLT NOC Bot

Alerta cuando â‰¥{}% de las activas de un PON estÃ¡ en LOS.
TambiÃ©n avisa por ONT individual: alta realizada (con potencia), LOS y recuperada (con potencia y hora, confirmada un ciclo despuÃ©s).
ONTs con â‰¥{} dÃ­as seguidos en LOS se consideran crÃ³nicas y dejan de generar avisos.

Comandos:
â€¢ /onts â€” Ver ONUs por OLT y puerto
â€¢ /cajas â€” Ver PONs con LOS ahora
â€¢ /los â€” Ver todas las ONTs en LOS (sin crÃ³nicas)
â€¢ /status â€” Resumen global
â€¢ /api â€” Uso de la API

_CachÃ©: hace {} min_r!   )r;   r#   rÅ   r—   re   )r9   r   r   r   Ú	cmd_start¥  s    rË   c          	   C   s™   t  ƒ  } | s  t |  d ƒ d  St | ƒ } t d d „  | Dƒ ƒ } t d d „  | Dƒ ƒ } | | | } t |  d j | | | | t ƒ  d ƒ ƒ d  S)Nu   â�³ CachÃ© no disponible aÃºn.c             s   s3   |  ]) } | j  d  d ƒ j ƒ  d k r d Vq d S)rw   ri   rx   r   N)rT   rr   )rE   rm   r   r   r   ú	<genexpr>¼  s    zcmd_status.<locals>.<genexpr>c             s   s3   |  ]) } | j  d  d ƒ j ƒ  d k r d Vq d S)rw   ri   r�   r   N)rT   r“   )rE   rm   r   r   r   rÌ   ½  s    ur   ðŸ“Š ESTADO GLOBAL

Total ONUs: `{}`
ðŸŸ¢ Online: `{}`
âš ï¸� LOS: `{}`
ðŸ”´ Offline: `{}`

_Datos de hace {} min_r!   )rZ   r;   rN   Úsumr#   re   )r9   r[   r¤   rx   rŽ   r�   r   r   r   Ú
cmd_status¶  s    	rÎ   c       	      C   sb  t  ƒ  } | s  t |  d ƒ d  St | ƒ } g  } xO | j ƒ  D]A } t | ƒ } | d d k r? | t k r? | j | | f ƒ q? W| j d d d „  d d ƒ | sÇ t |  d	 j t	 ƒ  d
 ƒ ƒ d  Sd j t
 | ƒ ƒ } xX | D]P \ } } | d j | d | d | d | d | | d | d | d ƒ 7} qã W| d j t	 ƒ  d
 ƒ 7} t |  | ƒ d  S)Nu   â�³ CachÃ© no disponible aÃºn.rŽ   r   rœ   c             S   s   |  d S)Nr   r   )Úxr   r   r   r¨   Õ  s    zcmd_cajas.<locals>.<lambda>ÚreverseTu;   âœ… Sin caÃ­das de caja ahora mismo

_Datos de hace {} min_r!   u#   ðŸš¨ PONs con caÃ­da de caja ({})

u^   â€¢ `{}` B`{}`P`{}` â€” âš ï¸� {} LOS ({}% activas) / ðŸ”´ {} Offline / ðŸŸ¢ {} Online â€” {}
r    r¡   r¢   r�   rx   r£   z
_Datos de hace {} min_)rZ   r;   r¬   Úvaluesr±   rÅ   rQ   Úsortr#   re   rN   )	r9   r[   r«   Zcajasr¯   rµ   r¶   ÚpctÚsr   r   r   Ú	cmd_cajasÈ  s(    		?rÕ   c          
   C   s*  t  ƒ  } | s  t |  d ƒ d  St ƒ  } t j ƒ  } g  } xè | D]à } | j d ƒ pZ d j ƒ  j ƒ  d k rr qB t | ƒ } t	 | j | ƒ ƒ } | j d ƒ } | j d ƒ p´ d }	 d  }
 | rÜ | t j
 | d ƒ j }
 |
 d  k	 r÷ |
 t k r÷ qB | j d	 | d | pd
 d |	 d |
 i ƒ qB W| sPt |  d j t ƒ  d t ƒ ƒ d  S| j d d d „  ƒ d j t | ƒ ƒ } d  } xR| D]J} | d	 } | j d d ƒ } | j d d ƒ } | j d d ƒ } | j d d ƒ } | j d d ƒ } | | k r+| d 7} | d j | ƒ 7} | d 7} | } | d j | ƒ 7} | d j | | ƒ 7} | rm| d j | ƒ 7} | d j | d ƒ 7} | d d  k	 r«| d  j | d ƒ 7} | d! 7} | d" j t | d ƒ ƒ 7} qˆWt | ƒ d# k rü| d  d$ … d% } | d& j t ƒ  d t ƒ 7} t |  | ƒ d  S)'Nu   â�³ CachÃ© no disponible aÃºn.rw   ri   r�   r‹   ry   zN/Az%Y-%m-%d %H:%M:%Srm   Zdesconocidar¹   rž   uV   âœ… Sin ONTs en LOS ahora mismo

_Datos de hace {} min_
_(excluidas crÃ³nicas â‰¥{}d)_r!   rœ   c             S   sI   |  d j  d d ƒ t |  d j  d d ƒ ƒ t |  d j  d d ƒ ƒ f S)Nrm   r©   ri   r¡   r¢   )rT   r]   )rÏ   r   r   r   r¨   	  s    zcmd_los.<locals>.<lambda>u    âš ï¸� ONTs en LOS â€” {} total
r©   r¡   r¢   rŠ   z
Sin nombrer²   u>   
â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�
u   ðŸ“¡ {}
u=   â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�â”�
u	   
ðŸ‘¤ {}
u   ðŸ“¦ Board `{}` / PON `{}`
u   ðŸ“� {}
u   â�± CayÃ³: `{}`z _({}d)_r³   u   ðŸ”‹ Potencia previa: `{}`
i   in  u   

âš ï¸� _Lista truncada_u8   
_Datos de hace {} min_ â€” _excluidas crÃ³nicas â‰¥{}d_)rZ   r;   r‡   r   r’   rT   r   r“   r{   rz   r•   r–   r—   rQ   r#   re   rÒ   rN   rv   )r9   r[   r|   rG   Zen_losrm   rœ   r}   r‹   ry   rž   r¶   Z
olt_actualÚitemr    r¡   r¢   r·   r¸   r   r   r   Úcmd_losã  sn    		' 



!r×   c             C   s|   t  ƒ  } | s  t |  d ƒ d  St t d d „  | Dƒ ƒ ƒ } d d „  | Dƒ } t |  d j t ƒ  d ƒ d | i ƒ d  S)	Nu   â�³ CachÃ© no disponible aÃºn.c             s   s0   |  ]& } | j  d  ƒ r | j  d  d ƒ Vq d S)r©   ri   N)rT   )rE   rm   r   r   r   rÌ   1  s    zcmd_onts.<locals>.<genexpr>c             S   s7   g  |  ]- } d  d j  | ƒ d d j  | ƒ i g ‘ q S)r1   u   ðŸ“¡ {}Úcallback_datazOLT:{})r#   )rE   r    r   r   r   rI   2  s   	 zcmd_onts.<locals>.<listcomp>z&Selecciona OLT:
_Datos de hace {} min_r!   Úinline_keyboard)rZ   r;   ra   r‘   r#   re   )r9   r[   ÚoltsÚkbr   r   r   Úcmd_onts,  s    	rÜ   c             C   s7   t  ƒ  } t |  d j | t d t ƒ  d t ƒ ƒ d  S)Nu¨   ðŸ“ˆ Uso API

get_all_onus_details: `{}/15` esta hora
Intervalo monitoreo: `{} min`
CachÃ© actualizado: `hace {} min`

_Umbral alerta: â‰¥{}% LOS sobre activas por PON_r!   )rX   r;   r#   rÉ   re   rÅ   )r9   Zusadasr   r   r   Úcmd_api5  s    	rÝ   c                s9  |  j  d ƒ r t |  d ƒ |  d d d } | t k rL t | ƒ rL d  S|  d d } |  d j d d ƒ } t ƒ  } | d d	 k rôt | ƒ d
 k rô| d ‰ t ‡ f d d †  | Dƒ ƒ } | sç t | | d j	 ˆ ƒ ƒ d  St
 | j ƒ  d d d „  ƒ} g  } x´ | D]¬ } t | ƒ }	 |	 t k r6d n | d d k rLd n d }
 d j	 |
 | d | d | d | d | d | d ƒ } | j d | d d j	 ˆ | d | d ƒ i g ƒ qWt | | d j	 ˆ t ƒ  d  ƒ d! d" | i ƒnA| d d# k r5t | ƒ d$ k r5| \ } ‰ ‰  ‰ ‡  ‡ ‡ f d% d †  | Dƒ } | sat | | d& ƒ d  Sd' ˆ d ˆ  d ˆ d( | d j  d) d* ƒ d t | ƒ d d d d d d d+ g  d, g  d- g  i } x­ | D]¥ } | j  d. d* ƒ j ƒ  } | j ƒ  d/ k r| d d 7<| d+ j | ƒ qÆ| j ƒ  d k rL| d d 7<| d- j | ƒ qÆ| d d 7<| d, j | ƒ qÆWt | ƒ }	 |	 t k r�d0 n d1 j	 ˆ ƒ } d2 j	 | ƒ } | d3 j	 ˆ ƒ 7} | d4 j	 ˆ  ˆ ƒ 7} | d5 j	 | d( ƒ 7} | d6 j	 | d ƒ 7} | d7 j	 | d |	 ƒ 7} | d8 j	 | d ƒ 7} | d9 j	 | d ƒ 7} t ƒ  } | d+ rô| d: 7} xŠ | d+ D]~ } | j  d; d< ƒ } | j  d= d* ƒ } t | | ƒ } | d> j	 | ƒ 7} | r×| d? j	 | ƒ 7} | d@ j	 t | ƒ ƒ 7} qrW| d, rw| dA 7} xl | d, D]` } | j  d; d< ƒ } | j  d= d* ƒ } | dB j	 | ƒ 7} | ri| d? j	 | ƒ 7} | dC 7} qW| d- rÝ| dD 7} xO | d- D]C } | j  d; d< ƒ } t | | ƒ } | dE j	 | t | ƒ ƒ 7} q–Wt | ƒ dF k r| d  dG … dH } | dI j	 t j ƒ  j dJ ƒ ƒ 7} t | | | ƒ d  S)KNr‰   Úmessager9   r<   r-   ú:rp   r   ZOLTé   r   c                s+   g  |  ]! } | j  d  ƒ ˆ  k r | ‘ q S)r©   )rT   )rE   rm   )r    r   r   rI   S  s   	 z#handle_callback.<locals>.<listcomp>zSin ONUs en `{}`.rœ   c             S   s.   |  d |  d j  ƒ  r' t |  d ƒ n d f S)Nr¡   r¢   r   )Úisdigitrd   )rÏ   r   r   r   r¨   Y  s    z!handle_callback.<locals>.<lambda>u   ðŸš¨rŽ   u   âš ï¸�u   ðŸŸ¢u1   {} B{}/PON{} â€” {}âœ… {}âš ï¸� {}ðŸ”´ ({} total)r¡   r¢   rx   r�   r¤   r1   rØ   zPON:{}:{}:{}u8   ðŸ“¡ {}
Selecciona un puerto PON:
_Datos de hace {} min_r!   r4   rÙ   ZPONr    c                sg   g  |  ]] } | j  d  ƒ ˆ k r t | j  d d ƒ ƒ ˆ  k r t | j  d d ƒ ƒ ˆ k r | ‘ q S)r©   r¡   ri   r¢   )rT   r]   )rE   rm   )r¡   r    r¢   r   r   rI   j  s   	 zSin ONUs en este puerto.r    r£   rª   ri   r¥   r¦   r§   rw   r�   u   ðŸš¨ CAÃ�DA DE CAJA DETECTADAu   ðŸ“Š PON {} â€” detallez{}

u   ðŸ“¡ OLT: `{}`
u   ðŸ“¦ Board `{}` / PON `{}`
u   ðŸ“� Zona: {}

u   ðŸŸ¢ Online: {}
u$   âš ï¸� LOS: {} ({}% de las activas)
u   ðŸ”´ Offline: {}
zTotal PON: {}
u   
âš ï¸� ONUs en LOS:
rŠ   z
Sin nombrer²   u	   âš ï¸� {}u    â€” {}u    â€” ðŸ”‹ `{}`
u   
ðŸ”´ ONUs Offline:
u   ðŸ”´ {}r³   u   
ðŸŸ¢ ONUs Online:
u   ðŸŸ¢ {} â€” ðŸ”‹ `{}`
i   in  u   

âš ï¸� _Lista truncada_u	   
â�± `{}`z%Y-%m-%d %H:%M:%S)rT   r?   r5   rB   r   rZ   rN   r¬   r>   r#   ra   rÑ   r±   rÅ   rQ   re   r   r“   rr   r‡   r~   rv   r   r’   r”   )Zcqr9   r=   Úpartsr[   r«   Ú	ordenadosrÛ   rÔ   rµ   ÚiconoZlabelÚ_Zonus_ponr¯   rm   r´   r¶   r|   r·   r¸   r¹   r   )r¡   r    r¢   r   Úhandle_callbackC  s¨    	"
.97	"!	





 "ræ   c              C   sî  t  d i  ƒ t j d ƒ d }  xÇy~t j d j t ƒ d d |  d d i d d	 ƒ} xG| j ƒ  j d
 g  ƒ D]-} | d d }  d | k r‚| d d d } | d j d d ƒ j d ƒ d j	 ƒ  } t j d | | ƒ t
 | ƒ sì qo | d k rt | ƒ qœ| d k rt | ƒ qœ| d  k r7t | ƒ qœ| d k rPt | ƒ qœ| d k rit | ƒ qœ| d k rœt | ƒ qo d | k ro t | d ƒ qo WWq# t k
 rå} z" t j d | ƒ t j d ƒ WYd  d  } ~ Xq# Xq# Wd  S)!NZdeleteWebhooku   ðŸ¤– Polling iniciador   z{}/getUpdatesrS   Úoffsetr%   é   é#   ÚresultZ	update_idr   rÞ   r9   r‰   r1   ri   ú@z
Cmd: %s %sz/startú/puertosú/ontsú/estadoú/statusz/cajasz/losz/apiZcallback_queryzpoll: %srÇ   )rì   rí   )rî   rï   )r/   r*   r_   r'   rT   r#   r(   r$   r   r   rB   rË   rÜ   rÎ   rÕ   r×   rÝ   ræ   r)   r+   rK   rÈ   )rç   rV   Úur9   r1   r.   r   r   r   Úpollº  sD    	)      rñ   c                s   t  ƒ  } | s d St t d d „  | Dƒ ƒ ƒ } | s< d S|  sÐ d j t ƒ  d ƒ g } xX t | d ƒ D]G \ } ‰  t ‡  f d d †  | Dƒ ƒ } | j d	 j | ˆ  | ƒ ƒ qk W| j d
 ƒ d j | ƒ Sy4 t	 |  d ƒ } | | d ‰  | d k  rt
 ‚ Wn t t
 f k
 r"d SYn Xt ‡  f d d †  | Dƒ ƒ } | sUd j ˆ  ƒ St | j ƒ  d d d „  ƒ} t |  ƒ d k rWd j ˆ  ƒ g } xš t | d ƒ D]‰ \ }	 }
 t |
 ƒ } | t k rÑd n |
 d d k rçd n d } | j d j |	 | |
 d |
 d |
 d |
 d |
 d |
 d ƒ ƒ q§W| j d j | ƒ ƒ d j | ƒ Sy4 t	 |  d ƒ } | d k  r|t
 ‚ | | d }
 Wn) t t
 f k
 r¶d  j |  d ƒ SYn Xt d! d" d# d$ d! t i d% d i d& d' j ˆ  |
 d |
 d ƒ i ƒ d" S)(a4  
    Equivalente en texto de los botones de /onts. Devuelve el texto a
    publicar, o None si ya lo ha publicado handle_callback.

    El orden de las listas es el mismo que el de los botones, asi que el
    numero que se teclea corresponde siempre a la misma OLT o PON
    mientras el cache no cambie.
    u   â�³ CachÃ© no disponible aÃºn.c             s   s0   |  ]& } | j  d  ƒ r | j  d  d ƒ Vq d S)r©   ri   N)rT   )rE   rm   r   r   r   rÌ   ê  s    z_onts_texto.<locals>.<genexpr>u   No hay OLTs en el cachÃ©.u3   ðŸ“¡ **OLTs disponibles** â€” datos de hace {} min
r!   r   c             3   s*   |  ]  } | j  d  ƒ ˆ  k r d Vq d S)r©   r   N)rT   )rE   rm   )r    r   r   rÌ   ó  s    u   `{:>2}` Â· {} â€” {} ONUsz2
Escribe `!onts <numero>` para ver sus puertos PONr³   r   z;Numero de OLT no valido. Escribe `!onts` para ver la lista.c                s+   g  |  ]! } | j  d  ƒ ˆ  k r | ‘ q S)r©   )rT   )rE   rm   )r    r   r   rI      s   	 z_onts_texto.<locals>.<listcomp>zSin ONUs en `{}`.rœ   c             S   s4   |  d t  |  d ƒ j ƒ  r- t |  d ƒ n d f S)Nr¡   r¢   r   )r]   rá   rd   )rÏ   r   r   r   r¨     s   z_onts_texto.<locals>.<lambda>u   ðŸ“¡ **{}** â€” puertos PON
u   ðŸš¨rŽ   u   âš ï¸�u   ðŸŸ¢u9   `{:>2}` {} B{}/PON{} â€” {}âœ… {}âš ï¸� {}ðŸ”´ ({} total)r¡   r¢   rx   r�   r¤   z,
Escribe `!onts {} <numero>` para el detallez>Numero de PON no valido. Escribe `!onts {}` para ver la lista.r‰   NrÞ   r9   r<   r-   zPON:{}:{}:{})rZ   ra   r‘   r#   re   Ú	enumeraterÍ   rQ   r   rd   Ú
IndexErrorru   r¬   rÑ   rN   r±   rÅ   ræ   r5   )Úargsr[   rÚ   ZlineasÚiÚnZi_oltr«   rã   ÚjÚstrÓ   rä   Zn_ponr   )r    r   Ú_onts_textoÝ  sb    		 
	.'$rù   c             C   s¸   |  j  ƒ  j ƒ  } | d j ƒ  } | d k rB t | d d … ƒ Sd t d t d t d t d t d t d	 t d
 t d t	 i	 } | j
 | ƒ } | sª d d j t | ƒ ƒ S| t ƒ d S)a7  
    Traduce '!x' al cmd_x correspondiente. Se reutilizan las mismas
    funciones que atienden a Telegram: se les pasa MM_CHAT como destino
    y send() se encarga de enrutar.

    La autorizacion aqui la da la pertenencia al canal de Mattermost, que
    es mas fino que el filtro por chat_id de Telegram.
    r   ú!puertosú!ontsr   Nz!startz!estadoz!statusz!cajasz!losz!apiz$Comando no reconocido. Disponibles: z, )rú   rû   )r   r   rr   rù   rË   rÜ   rÎ   rÕ   r×   rÝ   rT   r   ra   r5   )ZtextoZautor_idZpost_idZpartesZordenZtablaZfuncionr   r   r   Úcomando_mattermost%  s$    	
rü   c               C   s=   y t  j t ƒ Wn% t k
 r8 t j d d d ƒYn Xd S)zsHilo de escucha. Si Mattermost falla, reconecta solo; el resto del
    bot sigue funcionando pase lo que pase aqui.zHilo de Mattermost muerto.Úexc_infoTN)r6   Zescucharrü   r)   r*   r+   r   r   r   r   Úloop_mattermostH  s    rþ   Ú__main__uE   ðŸš€ Bot iniciado â€” umbral LOS %d%% sobre activas â€” intervalo %dsrÂ   Zdaemonz+Mattermost activo en paralelo con Telegram.z)Mattermost no configurado: solo Telegram.éð   )rC   rD   )rf   rg   rh   )^r	   r'   rK   r$   Z	threadingZloggingr   Úcollectionsr   r   r¼   ÚImportErrorr6   r5   r   r   rU   Z	BOT_TOKENrd   ZCHAT_IDZGROUP_IDr8   r   r†   rÅ   rÀ   r—   rÉ   ZbasicConfigÚINFOZFileHandlerZStreamHandlerZ	getLoggerr*   r#   r(   r/   r;   r>   r?   r@   rB   ÚlistrM   ZLockrL   rJ   rP   rR   rW   rX   rY   r^   r`   rZ   rc   re   rl   rn   rv   r~   r�   r…   r‡   rˆ   r{   rz   rŸ   r¬   r±   rº   r»   r½   r¾   r¿   rÃ   rÆ   rÊ   rË   rÎ   rÕ   r×   rÜ   rÝ   ræ   rñ   rù   rü   rþ   Ú__name__r_   ZThreadÚstartZconfigurador   r   r   r   Ú<module>   sÂ   			
9'	6E7I	w#H#	
