
    (<iE5                     n   U d 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	 ddl
Z
 e	e      j                         j                  Z ee      ej                   vr"ej                   j#                  d ee             ddlmZ ej*                  j-                  dd      Z e	e d      Z e	e d	      Z e	e d
      Z e	e d      ZdZdZdZ e ed            Z	 ddlZ ee j                   vre j                   j#                  de       ddl!m"Z# i  e#       ddiZ$e%eedz  f   e&d<   deddfdZ(d ededz  fd!Z)d"e%de*fd#Z+d"e%de,fd$Z-de%fd%Z.d&eddfd'Z/d(ededz  fd)Z0d&ede	dz  fd*Z1ded+ede,fd,Z2d-ede,fd.Z3d2d/Z4d2d0Z5e6d1k(  r e5        yy# e'$ r ddddddddddd
Z$Y w xY w)3u*  bot-activity.json 실시간 감시 → 완료 즉시 감지 시스템

bot-activity.json의 processing → idle 전환을 실시간 감지하여:
1. .done 파일이 존재하면 → 즉시 텔레그램 알림
2. done-protocol.log에 이벤트 기록

Usage:
    python3 scripts/activity-watcher.py
    N)datetime	timedeltatimezone)Path)format_notification_messageWORKSPACE_ROOTz/home/jay/workspacez /memory/events/bot-activity.jsonz/memory/eventsz/logs/done-protocol.logz/logs/activity-watcher.pid   i,     	   )hours)build_bot_team_map
anu-directBOT_TEAM_MAPdev1dev2dev3dev4dev5dev6dev7dev8)
r   r   r   r   r   r   r   r   anur   messagereturnc                 H   t        j                  t              j                         }d| d|  d}	 t        j
                  j                  dd       t        t        t              dd      5 }|j                  |       d	d	d	       y	# 1 sw Y   y	xY w# t        $ r Y y	w xY w)
u   done-protocol.log에 기록[z] [activity-watcher] 
Tparentsexist_okautf-8encodingN)r   nowKST	isoformatDONE_PROTOCOL_LOGparentmkdiropenstrwriteOSError)r   tslinefs       I/home/jay/workspace/.worktrees/task-2057-dev2/scripts/activity-watcher.pylog_protocolr3   C   s    	c		$	$	&Brd'y3D  &&td&C#'(#@ 	AGGDM	 	 	 s/   <B .B	 B 	BB B 	B! B!	since_strc                     	 | j                  d      r4t        j                  | d      j                  t        j
                        S t        j                  |       S # t        t        f$ r Y yw xY w)u   since 필드 파싱 (ISO 8601)Z%Y-%m-%dT%H:%M:%SZ)tzinfoN)	endswithr   strptimereplacer   utcfromisoformat
ValueError	TypeError)r4   s    r2   parse_since_timer@   O   sf    c"$$Y0DEMMU]UaUaMbb%%i00	" s   AA A A.-A.datac           	         | j                  di       }d}t        j                  t        j                        }|j                         D ]  \  }}|j                  dd      }|j                  dd      }|dk7  r0t        |      }|s>||z
  }	|	j                         dz  }
|
t        kD  s`t        d	| d
t        |
       d       d| d   |   d<   |j                  d      | d   |   d<   |dz  } |S )u   stuck 상태 봇 확인 및 자동 복구 (워치독 로직)

    Args:
        data: bot-activity.json 데이터

    Returns:
        복구된 봇 수
    botsr   statusidlesince 
processing<   [WATCHDOG] u   : processing → idle (timeout zm)r7      )getr   r%   r   r<   itemsr@   total_secondsTIMEOUT_MINUTESr3   intstrftime)rA   rC   recovered_countr%   bot_namebot_datarD   r4   
since_timeelapsedelapsed_minutess              r2   check_and_recover_stuck_botsrX   Y   s    88FBDO
,,x||
$C"jjl !(h/LL"-	 \! &i0
 
"!//1B6 _,;xj0OPSTcPdOeeghi/5DL"8,.1ll;O.PDL"7+q O-!0     c                    	 t         j                  d      }t        |dd      5 }t        j                  | |dd       ddd       |j                  t                y	# 1 sw Y   xY w# t        $ r}t        d
|        Y d}~yd}~ww xY w)u+   bot-activity.json 저장 (원자적 쓰기)z.tmpwr"   r#      Findentensure_asciiNTu(   ERROR: bot-activity.json 저장 실패: )BOT_ACTIVITY_FILEwith_suffixr+   jsondumpr;   r.   r3   )rA   	temp_filer1   es       r2   save_bot_activityrf      s    %11&9	)S73 	=qIIdAae<	=+,	= 	=  ?sCDs-   #A) AA) A&"A) )	B
2BB
c                     	 t         j                         sdi iS t        t         dd      5 } t        j                  |       cddd       S # 1 sw Y   yxY w# t        j
                  t        f$ r}t        d|        di icY d}~S d}~ww xY w)u   bot-activity.json 로드rC   rr"   r#   Nu(   ERROR: bot-activity.json 로드 실패: )r`   existsr+   rb   loadJSONDecodeErrorr.   r3   )r1   re   s     r2   load_bot_activityrl      s     '')B<#S7; 	 q99Q<	  	  	   '* ?sCD|s?   A A A	A AA A B0BBBrS   c                    	 t               }| |j                  di       vryt        j                  t        j
                        j                  d      }||d   |    d<   t        j                  j                  dd       t        t        dd	      5 }t        j                  ||d
d       ddd       t        |  d|        y# 1 sw Y   xY w# t        $ r}t        d|  d|        Y d}~yd}~ww xY w)u6   봇의 since 필드를 현재 UTC 시각으로 갱신.rC   Nr7   rF   Tr   r[   r"   r#   r\   Fr]   u   : since 갱신 → u    ERROR: since 갱신 실패 (bot=): )rl   rL   r   r%   r   r<   rQ   r`   r)   r*   r+   rb   rc   r3   	Exception)rS   rA   utc_nowr1   re   s        r2   update_bot_sincerq      s    J "488FB// ,,x||,556JK*1VXw' 	  &&td&C#S7; 	=qIIdAae<	= 	z!4WI>?	= 	=  J7zQCHIIJs5   C A0C C+C C	C 	C4C//C4teamc                    	 t        t         d      }|j                         syt        j                  |j                  d            }|j                  di       j                         D ]5  \  }}|j                  d      |  dk(  s|j                  d      d	k(  s3|c S  |j                  di       j                         D ]2  \  }}|j                  d      | k(  s|j                  d      d	k(  s0|c S  	 y# t        $ r Y yw xY w)
u:   task-timers.json에서 해당 팀의 활성 task_id 조회z/memory/task-timers.jsonNr"   r#   tasksteam_id-teamrD   running)	r   r   ri   rb   loads	read_textrL   rM   ro   )rr   
timer_filerA   task_id	task_datas        r2   get_active_task_for_teamr}      s   ^,,DEF
  "zz*...@A"&((7B"7"="="? 	GY}}Y'dV5>9immH>UYb>b	 #'((7B"7"="="? 	GY}}Y'4/IMM(4Ky4X	
   s;   "C9 A"C9 C9 C9 !;C9 C9 2C9 6C9 9	DDc                     t         j                  |       }|syg d}t        |      rBt         dz  }|j	                         r&|j                         st        fd|D              s|S y)u,   해당 봇의 팀 기반 .done 파일 찾기N)z.ackedz.clearz	.notifiedz
.escalatedz.processing.donec              3   V   K   | ]   }t          d | z  j                          " yw)r   N
EVENTS_DIRri   ).0extactive_tasks     r2   	<genexpr>z!find_done_file.<locals>.<genexpr>   s*     esU3%&@@HHJes   &))r   rL   r}   r   ri   
is_symlinkany)rS   rr   processed_exts	done_filer   s       @r2   find_done_filer      sl    H%DSN*40KK=!66	((*eVdeerY   chat_idc                 D   t         j                  j                  d      }|st        d       yd| d}|| d}t	        d      D ]  }	 t        j                  ||d	      }|j                  d
k(  r y|j                  dk(  r;t        |j                  j                  dd            }t        j                  |       wt        d|j                           y# t
        j                  $ r6}t        d|dz    d|        t        j                  d|z         Y d}~d}~ww xY w)uB   직접 Telegram Bot API 호출 (토큰 0, Claude 세션 미생성)ANU_BOT_TOKENu,   WARN: ANU_BOT_TOKEN 미설정, 알림 스킵Fzhttps://api.telegram.org/botz/sendMessage)r   textr	   
   )rb   timeout   Ti  zRetry-After   zWARN: Telegram API u&   WARN: Telegram 전송 실패 (attempt rK   rn   r\   N)osenvironrL   r3   rangerequestspoststatus_coderP   headerstimesleepRequestException)	r   r   	bot_tokenurlpayloadattemptrespretry_afterre   s	            r2   send_telegram_notificationr      s   

/ICD(<
@C!73G8 #	#==7B?D3&3&!$,,"2"2=!"DE

;'.t/?/?.@AB# 	 (( 	#A'A+cRSQTUVJJq'z""	#s%   'C1A	C;CD),DDr{   c                 :    t         |  dz  }|j                         S )u*   .done.notified 마커 파일 존재 확인z.done.notifiedr   )r{   notified_files     r2   check_already_notifiedr      s"    G9N!;;M!!rY   c                     	 t         j                  j                  dd       t        t         d      5 } | j	                  t        t        j                                      ddd       y# 1 sw Y   yxY w# t        $ r Y yw xY w)u   PID 파일 작성Tr   r[   N)	PID_FILEr)   r*   r+   r-   r,   r   getpidr.   )r1   s    r2   write_pid_filer      sg    dT:(C  	&AGGC		$%	& 	& 	& s.   1A5 -A) A5 )A2.A5 2A5 5	B Bc                     t        t         d      } | j                         r	 t        | d      5 }|D ]  }|j	                         }|s|j                  d      r(d|v s-|j                  d      \  }}}|j	                         j	                  d      j	                  d      t        j                  |j	                         <    	 ddd       t        j                  j                  dd	      }t        j                  j                  d
      s0t        dt        j                         t        j                  d       t                t!        d       t#               }i }|j                  di       j%                         D ]  \  }	}
|
j                  dd      ||	<    t!        d|        t'        j&                         }	 	 t'        j&                         }||z
  t(        k\  r6t#               }t+        |      }|dkD  rt-        |       t!        d| d       |}t#               }i }|j                  di       j%                         D ]  \  }	}
|
j                  dd      ||	<    |j%                         D ]  \  }	}|j                  |	d      }|	dk(  r|dk(  s%|dk(  s+t!        |	 d       t/        |	       t1        |	      }|r|j2                  }t5        |      rt!        | d       wt6        j                  |	|	      }d}|}	 t9        j:                  |j=                  d            }|i }d|vr|r| dnd|d<   t        t         d| d      }tA        |||      }t!        | d |	 d!       tC        ||       t!        |	 d"        |jE                         }t'        jF                  tH               # 1 sw Y   xY w# t        $ r Y w xY w# t8        j>                  t        f$ r Y w xY w# tJ        $ r t!        d#       Y ytL        $ r1}t!        d$|        t'        jF                  tH               Y d}~d}~ww xY w)%u   메인 루프z
/.env.keysrh   #="'NCOKACDIR_CHAT_ID
6937032012r   u   [FATAL] ANU_BOT_TOKEN 환경변수가 설정되지 않았습니다. 해결: 'source /home/jay/workspace/.env.keys'를 실행하거나 start-activity-watcher.sh를 사용하세요.)filerK   u   activity-watcher 시작rC   rD   rE   u   초기 상태 로드 완료: r   rJ   u   개 봇 복구 완료r   rH   u#   : processing → idle 전환 감지u!   : 이미 알림 전송됨, 스킵r"   r#   ru   rv   z/memory/reports/z.mdu   : 완료 감지 (bot=u   , idle 전환)u/   : idle 전환, .done 없음 (비정상 종료?)u    activity-watcher 종료 (SIGINT)u   ERROR: 예외 발생: )'r   r   ri   r+   strip
startswith	partitionr   r   r.   rL   printsysstderrexitr   r3   rl   rM   r   WATCHDOG_CHECK_INTERVALrX   rf   rq   r   stemr   r   rb   rx   ry   rk   _format_notification_messager   copyr   POLL_INTERVALKeyboardInterruptro   )env_filer1   r0   key_valuer   prev_activityprev_statusesrS   rT   last_watchdog_checkcurrent_timecurr_activity	recoveredcurr_statusescurr_statusprev_statusr   r{   rr   	done_datadone_file_pathreport_pathr   re   s                             r2   mainr      s7    ~&j12H	h$ V VD::<DDOOC$8SD[(,s(;Q27++-2E2Ec2J2P2PQT2U

399;/	VV jjnn/>G::>>/*~	

 	 *+ &'MM+//;AAC A("*,,x"@hA 0@A ))+
J	&99;L115LL 1 38G	q=%m4 ;yk9N!OP&2# ./MM&3&7&7&C&I&I&K I"(*2,,x*Hh'I *7)<)<)> +c%++//&A u$ ,.;&3H H:-P!QR %X. !/x 8I "+.. 2':(G94U)VW $0#3#3Hh#GD(,I-6N%,0JJ~7O7OY`7O7a,b	  )0,.	(	9IM$u~SW	) 4*..1AAQRYQZZ]/^*_K&B7KYb&cG )G94I(Sa)bc6wH$z1`%abW+c\ *..0M JJ}%I GV V  		Z %)$8$8'#B % $%, ! 	;< 	&1!56JJ}%%	&s   O N5	N5N5 A$N5O >CO1 O1 A&O1 ?%O$BO1 5N?:O 	OOO.+O1 -O..O1 1Q Q 'P;;Q __main__)r   N)7__doc__rb   r   r   r   r   r   r   pathlibr   r   __file__resolver)   _SCRIPTS_DIRr,   pathinsertreport_utilsr   r   r   rL   r   r`   r   r(   r   r   r   rO   r&   _sysutils.org_loaderr   _build_bot_team_mapr   dict__annotations__ImportErrorr3   r@   rP   rX   boolrf   rl   rq   r}   r   r   r   r   r   __name__ rY   r2   <module>r      sm    	 
  2 2   H~%%'..|CHH$HHOOAs<() T  02GHN++KLM ^$N34
N++BCD >""<=> yq!"TYY&		N+J+

+d+L$sC$J' &	# 	$ 	 4 %t % %P
D 
T 
	4 	Js Jt J,3 3: &S TD[ $ c d 6"C "D "u&p zF I
  Ls   "AF F43F4