sync: Auto-sync from ACG-M-L5090 at 2026-03-10 19:11:00
Synced files: - Quote wizard frontend (all components, hooks, types, config) - API updates (config, models, routers, schemas, services) - Client work (bg-builders, gurushow) - Scripts (BGB Lesley termination, CIPP, Datto, migration) - Temp files (Bardach contacts, VWP investigation, misc) - Credentials and session logs - Email service, PHP API, session logs Machine: ACG-M-L5090 Timestamp: 2026-03-10 19:11:00 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
100
temp/bardach_deleted_contacts.py
Normal file
100
temp/bardach_deleted_contacts.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""Search for deleted contacts in Barbara's mailbox using subprocess curl calls."""
|
||||
import subprocess, json, sys, urllib.parse
|
||||
|
||||
CLAUDE_APP = "fabb3421-8b34-484b-bc17-e46de9703418"
|
||||
CLAUDE_SECRET = "~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO"
|
||||
TENANT_ID = "dd4a82e8-85a3-44ac-8800-07945ab4d95f"
|
||||
USER = "barbara@bardach.net"
|
||||
|
||||
def curl_get(url, token, extra_headers=None):
|
||||
cmd = ['curl', '-s', '-H', f'Authorization: Bearer {token}']
|
||||
if extra_headers:
|
||||
for k, v in extra_headers.items():
|
||||
cmd.extend(['-H', f'{k}: {v}'])
|
||||
cmd.append(url)
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
return json.loads(result.stdout) if result.stdout.strip() else {}
|
||||
|
||||
def curl_post(url, token, body):
|
||||
cmd = ['curl', '-s', '-X', 'POST', '-H', f'Authorization: Bearer {token}',
|
||||
'-H', 'Content-Type: application/json', '-d', json.dumps(body), url]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
return json.loads(result.stdout) if result.stdout.strip() else {}
|
||||
|
||||
# Get token
|
||||
print("[STEP 1] Getting token...")
|
||||
token_cmd = subprocess.run([
|
||||
'curl', '-s', '-X', 'POST',
|
||||
f'https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token',
|
||||
'-d', f'client_id={CLAUDE_APP}&client_secret={CLAUDE_SECRET}&scope=https://graph.microsoft.com/.default&grant_type=client_credentials'
|
||||
], capture_output=True, text=True)
|
||||
token = json.loads(token_cmd.stdout)['access_token']
|
||||
print("[OK] Token acquired")
|
||||
|
||||
# Method 1: Contact folders delta - finds deleted contacts
|
||||
print("\n[STEP 2] Using contacts delta to enumerate all contacts including deleted...")
|
||||
delta_url = f"https://graph.microsoft.com/beta/users/{USER}/contacts/delta?$select=displayName,emailAddresses"
|
||||
all_active = []
|
||||
all_removed = []
|
||||
page = 0
|
||||
|
||||
while delta_url:
|
||||
page += 1
|
||||
data = curl_get(delta_url, token)
|
||||
if 'error' in data:
|
||||
print(f"[ERROR] {data['error'].get('code')}: {data['error'].get('message','')[:200]}")
|
||||
break
|
||||
|
||||
items = data.get('value', [])
|
||||
for item in items:
|
||||
if '@removed' in item:
|
||||
all_removed.append(item)
|
||||
else:
|
||||
all_active.append(item)
|
||||
|
||||
delta_url = data.get('@odata.nextLink')
|
||||
delta_token_url = data.get('@odata.deltaLink')
|
||||
|
||||
if page % 5 == 0:
|
||||
print(f" Page {page}: {len(all_active)} active, {len(all_removed)} removed...")
|
||||
|
||||
if not delta_url:
|
||||
break
|
||||
|
||||
print(f"\n[RESULT] Delta scan complete:")
|
||||
print(f" Active contacts: {len(all_active)}")
|
||||
print(f" Deleted contacts: {len(all_removed)}")
|
||||
|
||||
if all_removed:
|
||||
print(f"\n First 20 deleted contacts:")
|
||||
for r in all_removed[:20]:
|
||||
name = r.get('displayName', '(no name)')
|
||||
rid = r.get('id', '?')[:30]
|
||||
reason = r.get('@removed', {}).get('reason', '?')
|
||||
print(f" {name} (reason: {reason})")
|
||||
|
||||
# Method 2: Check the Deleted Items folder for contact-class items
|
||||
# Using the search API which handles IPM.Contact items
|
||||
print(f"\n[STEP 3] Searching Deleted Items folder via search API...")
|
||||
search_body = {
|
||||
"requests": [{
|
||||
"entityTypes": ["message"],
|
||||
"query": {"queryString": "kind:contacts"},
|
||||
"from": 0,
|
||||
"size": 25
|
||||
}]
|
||||
}
|
||||
search_result = curl_post(f"https://graph.microsoft.com/v1.0/users/{USER}/search/query", token, search_body)
|
||||
if 'error' in search_result:
|
||||
print(f"[INFO] Search API: {search_result['error'].get('code')}: {search_result['error'].get('message','')[:200]}")
|
||||
elif search_result.get('value'):
|
||||
for resp in search_result['value']:
|
||||
hits = resp.get('hitsContainers', [{}])
|
||||
for hc in hits:
|
||||
total = hc.get('total', 0)
|
||||
print(f" Search found {total} contact-related items")
|
||||
for hit in hc.get('hits', [])[:10]:
|
||||
resource = hit.get('resource', {})
|
||||
print(f" {resource.get('subject', '?')}")
|
||||
else:
|
||||
print(f"[INFO] Search returned: {json.dumps(search_result)[:300]}")
|
||||
Reference in New Issue
Block a user