76 lines
2.8 KiB
Python
76 lines
2.8 KiB
Python
import urllib.request, urllib.parse, json, os
|
|
from collections import defaultdict
|
|
|
|
APP_ID = "fabb3421-8b34-484b-bc17-e46de9703418"
|
|
TENANT_ID = "dd4a82e8-85a3-44ac-8800-07945ab4d95f"
|
|
CLIENT_SECRET = os.environ["CLIENT_SECRET"]
|
|
USER_ID = "41d14430-feb4-4ae2-aed6-2bd4e6384ca7"
|
|
|
|
token_data = urllib.parse.urlencode({
|
|
'client_id': APP_ID, 'client_secret': CLIENT_SECRET,
|
|
'scope': 'https://graph.microsoft.com/.default', 'grant_type': 'client_credentials'
|
|
}).encode()
|
|
req = urllib.request.Request(f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token", data=token_data, method='POST')
|
|
with urllib.request.urlopen(req) as r:
|
|
token = json.loads(r.read())['access_token']
|
|
|
|
base = f'https://graph.microsoft.com/v1.0/users/{USER_ID}/contacts'
|
|
|
|
def patch_contact(cid, data):
|
|
body = json.dumps(data).encode()
|
|
req = urllib.request.Request(f'{base}/{cid}', data=body, method='PATCH',
|
|
headers={'Authorization': f'Bearer {token}', 'Content-Type': 'application/json'})
|
|
with urllib.request.urlopen(req) as r:
|
|
return r.status
|
|
|
|
# Fetch all contacts with blank displayName
|
|
url = f'{base}?$select=id,displayName,givenName,surname,companyName,emailAddresses,businessPhones,mobilePhone&$top=999'
|
|
blanks = []
|
|
while url:
|
|
req = urllib.request.Request(url, headers={'Authorization': f'Bearer {token}'})
|
|
with urllib.request.urlopen(req) as r:
|
|
data = json.loads(r.read())
|
|
for c in data.get('value', []):
|
|
dn = (c.get('displayName') or '').strip()
|
|
if not dn:
|
|
blanks.append(c)
|
|
url = data.get('@odata.nextLink')
|
|
|
|
print(f'Found {len(blanks)} contacts with blank displayName\n')
|
|
|
|
fixed = 0
|
|
skipped = 0
|
|
for c in blanks:
|
|
given = (c.get('givenName') or '').strip()
|
|
surname = (c.get('surname') or '').strip()
|
|
company = (c.get('companyName') or '').strip()
|
|
emails = [e.get('address', '') for e in c.get('emailAddresses', []) if e.get('address', '').strip()]
|
|
phones = list(filter(None, (c.get('businessPhones') or []) + [c.get('mobilePhone')]))
|
|
|
|
# Build display name from best available info
|
|
if given and surname:
|
|
new_name = f'{given} {surname}'
|
|
elif given:
|
|
new_name = given
|
|
elif surname:
|
|
new_name = surname
|
|
elif company:
|
|
new_name = company
|
|
elif emails:
|
|
# Use email local part as name
|
|
new_name = emails[0].split('@')[0].replace('.', ' ').replace('_', ' ').title()
|
|
else:
|
|
# Nothing useful - skip
|
|
skipped += 1
|
|
continue
|
|
|
|
try:
|
|
status = patch_contact(c['id'], {'displayName': new_name})
|
|
src = 'name' if (given or surname) else ('company' if company else 'email')
|
|
print(f' [OK] "{new_name}" (from {src}, status {status})')
|
|
fixed += 1
|
|
except Exception as e:
|
|
print(f' [ERROR] {new_name}: {e}')
|
|
|
|
print(f'\n=== DONE: Fixed {fixed}, Skipped {skipped} (no usable data) ===')
|