meal-order-manager/functions/sync_roster/handler.py
Adam Moussa 8935fc8f2d
Add roster sync Lambda, move API key to Secrets Manager, add Slack manifest (#3)
- Add sync-roster Lambda that auto-syncs employee roster from Slack
  channel membership (runs Monday 6:55am ET before menu publish)
- Move FormApiKey from CloudFormation parameter/env var to Secrets
  Manager (meal-order-manager/form-api-key) per security conventions
- Add Slack app manifest with required bot scopes
- Add get_channel_members() and get_user_info() to shared Slack module
- Add Lambda function ARN outputs to CloudFormation
- Add log group for sync-roster Lambda (60-day retention)
2026-05-12 19:21:47 -04:00

59 lines
1.7 KiB
Python

import os
from shared.db import get_roster, put_roster
from shared.slack import get_channel_members, get_user_info
def lambda_handler(event, context):
channel_id = os.environ.get("SLACK_CHANNEL_ID") or __import__("shared.secrets", fromlist=["get_parameter"]).get_parameter(os.environ["SLACK_CHANNEL_PARAM"])
member_ids = get_channel_members(channel_id)
if not member_ids:
return {"status": "error", "message": "No members found in channel"}
employees = []
skipped = 0
for uid in member_ids:
info = get_user_info(uid)
if not info or info.get("is_bot") or info.get("id") == "USLACKBOT":
skipped += 1
continue
profile = info.get("profile", {})
name = profile.get("real_name") or info.get("real_name", "")
email = profile.get("email", "")
if not name or not email:
skipped += 1
continue
employees.append({
"name": name,
"email": email,
"slack_user_id": uid,
})
employees.sort(key=lambda e: e["name"])
existing = get_roster()
existing_set = {(e["name"], e["email"], e["slack_user_id"]) for e in existing}
new_set = {(e["name"], e["email"], e["slack_user_id"]) for e in employees}
if existing_set == new_set:
return {
"status": "no_change",
"employee_count": len(employees),
}
put_roster(employees)
added = new_set - existing_set
removed = existing_set - new_set
return {
"status": "synced",
"employee_count": len(employees),
"added": len(added),
"removed": len(removed),
"skipped": skipped,
}