
    <jQ                     \   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ZddlZddl	m
Z
 ddlmZ ddlZddlZddlmZ ddlmZ ddlmZ  eej                            dd	                    Zed
z  Zedz  Zedz  Zedz  Zedz  Zedz  Zedz  ZdgZej                            dd          Z ej                            dd          Z!dZ" ej#        ej$        dd            ej%        d          Z&d Z'd Z(d Z)d Z*d Z+d Z,d+d!Z-d" Z.d# Z/d,d&Z0d-d(Z1d) Z2e3d*k    r e2             dS dS ).u  
McCoy Doors Gmail Integration — Phase 0
========================================
Minimal, silent inbox polling script.

Polls mccoydoors.ai@gmail.com via Gmail API (READ-ONLY),
matches against triage rules in triage-rules.yaml,
and creates Trello cards for matched emails on the
AGENT MCCOY 🇺🇸 Trello board (📬 Inbox list).

Designed to run as a cron job. READ-ONLY — never modifies inbox.
    N)parsedate_to_datetime)Path)Request)InstalledAppFlow)buildMCCOY_VAULTz(/home/chiefos/vaults/MCCOY_HQ/01_CONTEXTzgmail-api-credentialsscriptszclient_secret.jsonztoken.picklez
token.jsonztriage-rules.yamlzprocessed-ids.jsonz.https://www.googleapis.com/auth/gmail.readonlyTRELLO_API_KEY TRELLO_API_TOKENzhttps://api.trello.com/1z'%(asctime)s [%(levelname)s] %(message)sz%Y-%m-%d %H:%M:%S)levelformatdatefmtzgmail-watchc                     d} t                                           r	 t          t           d          5 }t          j        |          } ddd           n# 1 swxY w Y   t
                              d           n2# t          $ r%}t
                              d|           Y d}~nd}~ww xY w| t                                          r	 ddl
m} t          t                    5 }t          j        |          }ddd           n# 1 swxY w Y   |                    |t                    } t
                              d           n2# t          $ r%}t
                              d|           Y d}~nd}~ww xY w| t                                          s4t
                              d	t"                     t%          j        d
           t
                              d           t)          j        t-          t                    t                    }|                    d          } t          t           d          5 }t          j        | |           ddd           n# 1 swxY w Y   t
                              d           | r| j        r| j        r	 |                     t9                                 t          t           d          5 }t          j        | |           ddd           n# 1 swxY w Y   t
                              d           n2# t          $ r%}t
                              d|           Y d}~nd}~ww xY w| r| j        s.t
                              d           t%          j        d
           t=          dd|           }|S )z?Authenticate and return a Gmail API service object (read-only).Nrbz$Loaded credentials from token.picklezFailed to load token.pickle: %sr   )Credentialsz"Loaded credentials from token.jsonzFailed to load token.json: %sz4No credentials found. Place client_secret.json in %s   u/   No saved token found — starting OAuth flow...)portwbz!Saved credentials to token.picklezRefreshed and saved credentialszToken refresh failed: %su.   Authentication failed — no valid credentialsgmailv1)credentials)TOKEN_PICKLEexistsopenpickleloadloginfo	Exceptionwarning
TOKEN_JSONgoogle.oauth2.credentialsr   jsonfrom_authorized_user_infoGMAIL_SCOPESCLIENT_SECRETerrorCRED_DIRsysexitr   from_client_secrets_filestrrun_local_serverdumpexpiredrefresh_tokenrefreshr   validr   )credsfer   
token_dataflowservices          ?/home/chiefos/vaults/MCCOY_HQ/01_CONTEXT/scripts/gmail-watch.pyget_gmail_servicer;   @   sK   E  >	>lD)) 'QA' ' ' ' ' ' ' ' ' ' ' ' ' ' 'HH;<<<< 	> 	> 	>KK91========	> }**,,}	<======j!! *Q!Yq\\
* * * * * * * * * * * * * * *99*lSSEHH9:::: 	< 	< 	<KK7;;;;;;;;	< }##%% 	IILhWWWHQKKKBCCC8]9K9K\ZZ%%1%--,%% 	"Kq!!!	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	" 	"4555  7 75#6 7	7MM'))$$$lD)) &QE1%%%& & & & & & & & & & & & & & &HH67777 	7 	7 	7KK2A66666666	7   		BCCCGTu555GNs   A9 AA9 AA9 AA9 9
B(B##B(E !D6E DE 	D
8E 
E2E--E26III6L K&L &K**L -K*.L 
L;L66L;c                     t                                           s4t                              dt                      t	          j        d           t          t                     5 } t          j        |           }ddd           n# 1 swxY w Y   t          |t                    s.t                              d           t	          j        d           |S )z)Load and validate triage rules from YAML.zTriage config not found: %sr   Nz!Triage config must be a YAML dict)TRIAGE_CONFIGr   r   r(   r*   r+   r   yaml	safe_load
isinstancedict)r5   configs     r:   load_triage_configrC   {   s    !! 		/???	m		 #""# # # # # # # # # # # # # # # fd## 		5666Ms   "BB
Bc                     t                                           r	 t          t                     5 } t          j        |           }ddd           n# 1 swxY w Y   t          |                    dg                     S # t          j        t          f$ r%}t          
                    d|           Y d}~nd}~ww xY wt                      S )z0Load set of already-processed Gmail message IDs.N	processedu.   Corrupted processed-ids.json: %s — resetting)PROCESSED_IDSr   r   r$   r   setgetJSONDecodeErrorKeyErrorr   r!   )r5   datar6   s      r:   load_processed_idsrL      s     M	Mm$$ $y||$ $ $ $ $ $ $ $ $ $ $ $ $ $ $txxR00111$h/ 	M 	M 	MKKH!LLLLLLLL	M55Ls:   A> AA> AA> A%A> >B9B44B9c                 >   t           j                            dd           t          |           t	          j        dt	          j                              d}t          t           d          5 }t          j	        ||d           ddd           dS # 1 swxY w Y   dS )	z&Persist processed message IDs to disk.T)parentsexist_okz%Y-%m-%dT%H:%M:%SZ)rE   updatedw   )indentN)
rF   parentmkdirsortedtimestrftimegmtimer   r$   r/   )idsrK   r5   s      r:   save_processed_idsr[      s    td;;;?SUYU`UbUb1c1cddD	mS	!	! %Q	$!$$$$% % % % % % % % % % % % % % % % % %s   -BBBc                    | dS ddl m} g } ||           D ]\  }}t          |t                    ro	 |                    |                    |pdd                     H# t          t          f$ r- |                    |                    dd                     Y w xY w|                    |           d                    |          S )	z8Decode a potentially encoded email header to plain text.Nr   r   )decode_headerutf-8replaceerrors )	email.headerr]   r@   bytesappenddecodeLookupErrorUnicodeDecodeErrorjoin)valuedhpartspartcharsets        r:   r]   r]      s    }r000000EE  gdE"" 	ET[[);GI[NNOOOO!34 E E ET[[[CCDDDDDE LL88E??s   ,A  ;BBc                     |                                 }| D ]S}|                    dd                                           |k    r%t          |                    dd                    c S TdS )z2Extract a header value by name (case-insensitive).namer   rj   )lowerrH   r]   )headersrp   
name_lowerhs       r:   
get_headerru      so    J 5 555""$$
22 w!3!344444 32    ,  c                    |                      di           }|                     dd          }|rIddl}	 |                    |                              dd          }|d|         S # t          $ r Y nw xY w|                      d	g           }|D ]}|                     d
d          }|dk    ry|                     di           }|                     dd          }|rKddl}	 |                    |                              dd          }|d|         c S # t          $ r Y nw xY w|                     d	g           }	|	rt          ||          }
|
r|
c S dS )z9Extract a plain-text body preview from a message payload.bodyrK   r   r   Nr^   r_   r`   rl   mimeTypez
text/plainz(no plain text body))rH   base64urlsafe_b64decoderf   r    get_body_preview)payload	max_charsry   rK   r{   textrl   rm   mime	sub_partsresults              r:   r}   r}      s    ;;vr""D88FBD 	++D11888SSD

## 	 	 	D	 KK$$E  xx
B''<88FB''D88FB''D !33D99@@QZ@[[D

++++    D HHWb))	 	%dI66F !!s#   3A( (
A54A5!3D
D$#D$c                    |                                  }|                    dg           D ]a}|                    dd                                           }|r5||v r1|                    dd          |                    dd          d|d	c S b|                                 }|                    d
g           D ]a}|                    dd                                           }|r5||v r1|                    dd          |                    dd          d|d	c S b|                    di           }	|	                    dd          |	                    dd          ddd	S )a
  
    Apply triage rules to determine category and whether to create a card.

    Returns: dict with keys:
        - category: str
        - create_card: bool
        - rule_type: str  # 'subject', 'sender', or 'default'
        - matched_trigger: str (optional)
    subject_triggerswordr   categoryzOrder/Quotecreate_cardTsubject)r   r   	rule_typematched_triggerflag_only_sendersaddressFlaggedFsenderdefaultUnclassifiedN)rq   rH   )
r   r   rB   subject_lowertriggerr   sender_lowerruleaddrr   s
             r:   classify_emailr      s    MMOOM ::0"55  {{62&&,,.. 	DM))#KK
MBB&{{=$??&#'	     <<>>L

.33  xx	2&&,,.. 	DL(( HHZ;;#xxu==%#'	     jjB''GKK
N;;{{=$77	  rv   c                    t           rt          sdS |                    di           }|d         dk    r+|                    d|                    dd                    }n|                    dd          }|sdS | d	         d
d         }d|d          d| }d| d          d| d          d| d	          d|d          d|                    dd           ddd|                     dd          dd| d          dg}d                    |          }t           d}	t           t          |||d d!}
	 t          j        |	|
d"#          }|j        dk    rA|                                }d$|                    d%|                    d&d                    d
fS d'd
d(|j         d)|j	        d
d          fS # t
          j
        $ r}d'd
t          |          fcY d
}~S d
}~ww xY w)*z
    Create a Trello card on the McCoyDoorsMore.com board.

    Returns: (success: bool, card_url: str or None, error: str or None)
    )FNzOTrello API credentials not configured (set TRELLO_API_KEY and TRELLO_API_TOKEN)trellor   r   holding_list_iddefault_list_idr   )FNzNo Trello list ID configuredr   N   [z] z
**From:** r   z
**Date:** datez**Subject:** z**Category:** z**Matched:** r   N/Az---z**Body Preview:**body_previewz(no preview)z*Gmail Message ID: gmail_id*
z/cardsbottom)keytokenidListrp   descpos   )paramstimeoutTshortUrlurlFzTrello API error : )r
   r   rH   ri   TRELLO_BASErequestspoststatus_coder$   r   RequestExceptionr-   )
email_infoclassificationrB   
trello_cfglist_idr   	card_name
desc_parts	card_descr   r   resprK   r6   s                 r:   create_trello_cardr     s]     n!1 nmmHb))J j!^33..!2JNNCTVX4Y4YZZ..!2B77 ;:: #DSD)G;N:.;;';;I 	,Z)++)Z'))/
9-//5
355F**+<eDDFF
~~66
7j4777J 		*%%I
 
 
 C! F#}S<<<s""99;;D*dhhub.A.ABBDHH$ YD4D Y Y	RVSVRV Y YYY$ # # #dCFF"""""""#s%   /A"F/ F/ /G>GGGF
   c                 4   t                      }dddddg g d}	 d}|                                                                                     d||                                          }nR# t
          $ rE}t                              d|           |d                             d|            |cY d	}~S d	}~ww xY w|	                    d
g           }	t          |	          |d<   |	st                              d           |S t                              dt          |	                     |	D ]$}
|
d         }||v r|dxx         dz  cc<    	 |                                                                 	                    d|dg d                                          }nT# t
          $ rG}t                              d||           |d                             d| d|            Y d	}~d	}~ww xY w|	                    di           	                    dg           }t          |d          }t          |d          }t          |d          }	 t          |          }|                    d          }n# t
          $ r |r
|d	d         nd}Y nw xY wd}	 |                                                                 	                    d|d !                                          }t!          |	                    di                     }n5# t
          $ r(}d"}t                              d#||           Y d	}~nd	}~ww xY wt%          |||          }||||||d$         |d%         |	                    d&          |d'         |d	d(         d)
}|d*                             |           |d'         sot                              d+|d$         |d	d,         |d	d-         |	                    d&d.                     |                    |           |d/xx         dz  cc<   |||||d0}|rZt                              d1|d$         |d	d,         |d	d-                    |                    |           |d/xx         dz  cc<   Pt)          |||          \  }}}|r<t                              d2|d$         |d	d,         |           |d3xx         dz  cc<   n\t                              d4|d$         |d	d,         |           |d5xx         dz  cc<   |d                             d6| d|            |                    |           |d/xx         dz  cc<   &t+          |           t                              d7|d/         |d         |d3         |d5                    |S )8zo
    Poll the Gmail inbox, triage new emails, and optionally create Trello cards.

    Returns: stats dict
    r   )total_fetchedalready_processed
new_emailscards_createdcards_failedra   resultsin:inboxmeuserIdq
maxResultszGmail API list failed: %sra   zGmail list failed: Nmessagesr   zNo messages found in inboxzFetched %d messages from inboxidr   r   metadata)SubjectFromDateToCcr   r   r   metadataHeaderszFailed to fetch message %s: %szFetch failed for r   r~   rr   r   r   r   %Y-%m-%d %H:%M   unknownr   full)r   r   r   z(could not fetch body)zBody fetch failed for %s: %sr   r   r   r      )
r   r   r   r   date_rawr   r   r   r   r   r   u/     [FLAG ONLY] [%s] %s — from %s (trigger: %s)P   <   r   r   )r   r   r   r   r   z1  [DRY RUN] Would create card: [%s] %s (from: %s)u     [CARD] [%s] %s → %sr   u     [FAIL] [%s] %s — %sr   zCard failed for z:Done: %d new, %d already seen, %d cards created, %d failed)rL   usersr   listexecuter    r   r(   re   rH   lenr   r!   ru   r   rX   r}   debugr   addr   r[   )r9   rB   dry_runmax_resultsrE   statsqueryr   r6   r   msg_refmsg_idmsgrr   r   r   r   dtdate_strr   full_msgr   entryr   successcard_urlr(   s                              r:   
poll_inboxr   W  s    #$$I E))++00" 1 
 
 '))	 	
    		-q111h8Q88999
 zz*b))H ]]E/ -...HH-s8}}=== o! o! Y%&&&!+&&&	--//**,,00z G G G 1   gii C  	 	 	KK8&!DDD(O""#Dv#D#D#D#DEEEHHHH	
 '')R((,,Y;;Wi00GV,,gv..	@&x00B{{#344HH 	@ 	@ 	@(0?x}}iHHH	@ 	A}}//1155v 6  gii  ,HLLB,G,GHHLL 	A 	A 	A3LII4fa@@@@@@@@	A
 (@@  &z2'4-112CDD)-8(#.
 
 	i&&&m, 
	HHAz*ss""#4e<<   MM&!!!,1$ (
 

  		HHCz*ss	   MM&!!!,1$#5j.RX#Y#Y 5 	IHH)z*	   /"""a'""""KK)z*	   .!!!Q&!!!(O""#Gf#G#G#G#GHHHflq  y!!!HHDl!"on   Lsb   AA+ +
B:5:B5/B:5B:AF  
G1*=G,,G1$I44JJA1L
L9L44L9   c                    t                               d           t                               d|           t                               d           |                                                                                     dd|                                          }|                    dg           }t                               dt          |                     t                               d           |st                               d	           d
S t          |d          D ]\  }}|d         }|                                                                                     d|dg d                                          }|                    di                               dg           }t          |d          }	t          |d          }
t          |d          }	 t          |          }|                    d          }n'# t          $ r t          |          d
d         }Y nw xY wt                               d|           t                               d|           t                               d|           t                               d|
           t                               d|	           t                               d           t                               d           d
S )zATest authentication by fetching and printing recent inbox emails.u!   === GMAIL WATCH — TEST MODE ===z6Authenticated. Fetching %d most recent inbox emails...r   r   r   r   r   zFound %d messages in inboxzInbox is empty.Nr   r   r   )r   r   r   r   r~   rr   r   r   r   r   r   z  Email #%d:z    ID:      %sz    Date:    %sz    From:    %sz    Subject: %sz=== TEST COMPLETE ===)r   r   r   r   r   r   rH   r   	enumerateru   r   rX   r    r-   )r9   r   r   r   ir   r   r   rr   r   r   r   r   r   s                 r:   test_auth_and_listr      s   HH0111HHE{SSSHHRLLL]]__%%'',,zk -  gii  zz*b))HHH)3x==999HHRLLL "###!,,  
7mmoo&&((,,F:777 - 
 
 ')) 	
 '')R((,,Y;;Wi00GV,,gv..	*&x00B{{#344HH 	* 	* 	*8}}SbS)HHH	* 	###"F+++"H---"F+++"G,,,HH$%%%%%s   $H!H'&H'c            
         t          j        d          } |                     ddd           |                     ddd           |                     d	t          d
d           |                                 }t
                              d           t                      }t
                              dt                     t                      }t
                              d           |j
        rt          ||j                   d S t
                              d|j                   t          |||j        |j                  }t
                              d           t
                              d           t
                              d|d                    t
                              d|d                    t
                              d|d                    t
                              d|d                    t
                              d|d                    |d         r\t
                              d t          |d                              |d         d d!         D ]}t
                              d"|           |d#         D ]N}|d$         rd%nd&}t
                              d'||d(         |d)         d d*         |d+         d d,                    Od S )-Nu;   McCoy Doors Gmail Watch — silent inbox polling and triage)descriptionz--test
store_truezETest mode: authenticate and show recent emails without creating cards)actionhelpz	--dry-runzFRun triage logic but don't create Trello cards (log what would happen)z--max-resultsr   z.Maximum emails to fetch per poll (default: 10))typer   r   u#   McCoy Doors Gmail Watch — Phase 0zLoaded triage config from %sz(Gmail API authenticated (read-only mode))r   z"Polling inbox (max %d messages)...)r   r   r   z=== Summary ===z  Fetched:        %dr   z  Already seen:   %dr   z  New emails:     %dr   z  Cards created:  %dr   z  Cards failed:   %dr   ra   z  Errors:         %dr   z    - %sr   r   CARDFLAGz  [%s] [%s] %s (from: %s)r   r   r   r   r   )argparseArgumentParseradd_argumentint
parse_argsr   r   rC   r=   r;   testr   r   r   r   r   )parserargsrB   r9   r   errr   r   s           r:   mainr  1  s   $Q  F T    
 U    
 =	     DHH2333  !!FHH+];;;  !!GHH7888y 70@AAAA HH143CDDDw$JZ[[[E HHRLLLHHHH#U?%;<<<HH#U+>%?@@@HH#U<%8999HH#U?%;<<<HH#U>%:;;;X &'U8_)=)=>>>?2A2& 	& 	&CHHZ%%%%y! v v /;V,feJ6GyIYZ][]Z]I^`efn`opsqsps`tuuuuv vrv   __main__)rw   )Fr   )r   )4__doc__r   r$   loggingosr   rer*   rW   email.utilsr   pathlibr   r>   r   google.auth.transport.requestsr   google_auth_oauthlib.flowr   googleapiclient.discoveryr   environrH   VAULTr)   
SCRIPT_DIRr'   r   r"   r=   rF   r&   r
   r   r   basicConfigINFO	getLoggerr   r;   rC   rL   r[   r]   ru   r}   r   r   r   r   r  __name__ rv   r:   <module>r     s       				  				 



  - - - - - -         2 2 2 2 2 2 6 6 6 6 6 6 + + + + + +
 	RZ^^M+UVVWW**Y
//.($
0011@A  0"55:>>"4b99 (  
,4   
 g&&5 5 5v  (	 	 	% % %  "  "" "" "" ""P* * *`9# 9# 9#~c c c cR+& +& +& +&b7v 7v 7vt zDFFFFF rv   