Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

25 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🀝 New Starters MeetUp

A Slack /newintro command that books coffee-chat and buddy intro meetings for new starters β€” serverless, on two AWS Lambdas.

Python AWS Lambda Slack License CI

Features β€’ Architecture β€’ Quick Start β€’ Configuration β€’ Usage β€’ Contributing


✨ Features

Feature What it does
⚑ /newintro slash command Opens a Slack modal to book Coffee β˜•οΈ or Buddy 🀝 intros for a list of emails
πŸͺͺ Azure AD group sync Pulls the pool of intro partners from an Azure AD group and prunes departed users
βš–οΈ Weighted partner picking DynamoDB tracks how often each partner has been used and favours the least-used
πŸ“… Calendar-aware booking Google Calendar FreeBusy search finds a free 15-minute slot (11:00–15:00, weekdays)
πŸ” Business-day cadence Spaces each person's meetings two business days apart, skipping weekends
πŸ’¬ Live status updates Posts booking progress, per-meeting confirmations, and a final summary to the channel
⚑ Serverless Two AWS Lambdas (ARM64/Graviton) + a shared dependency layer β€” nothing to host

πŸ“‹ Prerequisites

Requirement Notes
Python 3.13 (matches the Lambda runtime)
AWS account Lambda, Secrets Manager, DynamoDB, CloudWatch
Slack app Bot token, signing secret, and a /newintro slash command
Azure AD app Client credentials with group read access
Google service account Domain-wide delegation for Calendar FreeBusy + event creation

Slack app requirements

  • Slash command /newintro pointing at the UI Lambda's URL
  • Bot token scopes: commands, chat:write

πŸ—ΊοΈ Architecture

Two Lambdas: a thin UI Lambda that answers Slack within its 3-second window, and a Worker Lambda that does the slow booking work asynchronously.

flowchart LR
    U["πŸ‘€ Slack user"] -->|"/newintro + modal"| UI["πŸŽ›οΈ UI Lambda<br/>src/ui_lambda/ui_entry.py"]
    UI -->|"async invoke"| W["βš™οΈ Worker Lambda<br/>src/worker_lambda/worker_entry.py"]
    SM["πŸ” Secrets Manager<br/>CONFIG_SECRET"] -.-> UI
    SM -.-> W
    W --> AAD["πŸͺͺ Azure AD<br/>group sync"]
    W --> DDB["πŸ—„οΈ DynamoDB<br/>partner weights"]
    W --> GC["πŸ“… Google Calendar<br/>FreeBusy + events"]
    W -->|"status messages"| CH["πŸ’¬ Slack channel"]
Loading

The worker, per email: syncs the Azure AD group into DynamoDB, picks the least-used available partner, searches Google Calendar for a free 15-minute slot, creates the event with both attendees, bumps the partner's weight, and posts the confirmation to Slack.


πŸš€ Quick Start

1. Clone the repository

git clone https://github.com/CaputoDavide93/New-Starters-Meetup.git
cd New-Starters-Meetup

2. Install the layer dependencies

The dependency layer is not committed β€” build it locally:

pip install -r Layer/requirements.txt --target Layer/python/

3. Build the deployment packages

./scripts/build.sh

scripts/build.sh does three things:

  1. Copies src/common/*.py into Layer/python/intro_common/ (the shared code ships inside the layer as the intro_common package)
  2. Copies the two entry files into deploy/ui-lambda/ and deploy/worker-lambda/ staging folders
  3. Zips everything into dist/: ui-lambda.zip, worker-lambda.zip, and layer-python313-arm64.zip

4. Deploy with the AWS CLI

# Publish the shared layer
aws lambda publish-layer-version \
  --layer-name newstarters-deps \
  --compatible-runtimes python3.13 \
  --compatible-architectures arm64 \
  --zip-file fileb://dist/layer-python313-arm64.zip

# Update the two functions (create them first with your preferred tooling)
aws lambda update-function-code --function-name intro-ui-lambda \
  --zip-file fileb://dist/ui-lambda.zip
aws lambda update-function-code --function-name intro-worker-lambda \
  --zip-file fileb://dist/worker-lambda.zip

Handlers: ui_entry.lambda_handler (UI) and worker_entry.lambda_handler (worker). Both functions need the layer attached and the environment variables below; the worker needs a generous timeout (it stops itself safely near the 15-minute Lambda cap).


βš™οΈ Configuration

Environment variables

Variable Lambda Required Description
CONFIG_SECRET both βœ… ARN of the AWS Secrets Manager secret holding the JSON config
WORKER_FUNCTION_NAME UI βœ… Name of the worker Lambda to invoke asynchronously
LOG_LEVEL worker ❌ Python log level (default INFO)

See .env.example for the full template.

The Secrets Manager secret

All application config lives in one JSON secret (CONFIG_SECRET), loaded once at cold start by src/common/config.py:

Key Required Description
slack_bot_token βœ… Slack bot OAuth token (xoxb-…)
slack_signing_secret βœ… Slack app signing secret
slack_trigger_channel βœ… Fallback channel ID for status messages
azure_tenant_id βœ… Azure AD tenant (coffee intros)
azure_client_id βœ… Azure AD app client ID
azure_client_secret βœ… Azure AD app client secret
azure_group_id βœ… Azure AD group holding coffee-intro partners
buddy_azure_tenant_id ❌ Buddy-intro override (falls back to the coffee credentials)
buddy_azure_client_id ❌ Buddy-intro override
buddy_azure_client_secret ❌ Buddy-intro override
buddy_azure_group_id βœ… Azure AD group holding buddy-intro partners
google_service_account_key βœ… Google service-account key, as a JSON string
google_delegated_user βœ… Workspace user the service account impersonates
google_calendar_id βœ… Calendar the events are created on
dynamodb_table_name βœ… Partner-weight table (coffee)
buddy_dynamodb_table_name βœ… Partner-weight table (buddy)
meeting_title_template ❌ Event title template, e.g. β˜•οΈ Coffee: {person1} & {person2}
meeting_description_template ❌ Event description template
buddy_meeting_title_template ❌ Buddy event title template
buddy_meeting_description_template ❌ Buddy event description template

Templates accept {person1}/{person2} (display names) and {email1}/{email2} placeholders; unknown placeholders fall back to a safe default title.


πŸ“– Usage

  1. In Slack, run /newintro in any channel the bot can post to.
  2. Fill in the modal:
    • Which type of intro? β€” β˜•οΈ Coffee or 🀝 Buddy
    • Participant emails β€” comma-separated list of new starters
    • Start date β€” first day to search for slots
    • Meetings per person β€” how many intros to book for each email
  3. Submit. The UI Lambda acknowledges instantly and hands off to the worker, which posts progress to the channel as it books:
β˜• Booking Coffee…
βœ… Jane.Doe ↔ John.Smith β€” 14 Jul 11:00
βœ… Booking complete! 2 succeeded, 0 failed.

Maintenance: DynamoDB cleanup

scripts/cleanup_db.py merges duplicate user records caused by email case mismatches:

python scripts/cleanup_db.py --table intro-weights --dry-run   # preview
python scripts/cleanup_db.py --table intro-weights --apply     # apply

πŸ“ Repo structure

New-Starters-Meetup/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ common/               # 🧠 shared code, shipped in the layer as `intro_common`
β”‚   β”‚   β”œβ”€β”€ config.py         # πŸ” Secrets Manager loader
β”‚   β”‚   β”œβ”€β”€ azure_sync.py     # πŸͺͺ Azure AD group sync
β”‚   β”‚   β”œβ”€β”€ calendar_utils.py # πŸ“… Google Calendar FreeBusy + events
β”‚   β”‚   └── dynamo_utils.py   # πŸ—„οΈ DynamoDB weight management
β”‚   β”œβ”€β”€ ui_lambda/
β”‚   β”‚   └── ui_entry.py       # πŸŽ›οΈ Slack slash command + modal handler
β”‚   └── worker_lambda/
β”‚       └── worker_entry.py   # βš™οΈ booking engine
β”œβ”€β”€ Layer/
β”‚   └── requirements.txt      # πŸ“¦ layer deps (install into Layer/python/, not committed)
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ build.sh              # πŸ”§ builds the three deployment ZIPs into dist/
β”‚   └── cleanup_db.py         # 🧹 DynamoDB duplicate-user cleanup
└── .env.example              # βš™οΈ Lambda environment template

deploy/ and dist/ are build staging/output folders created by scripts/build.sh β€” they are gitignored, never edited by hand.

CloudWatch logs

# Replace with your function names
aws logs tail /aws/lambda/intro-ui-lambda --follow
aws logs tail /aws/lambda/intro-worker-lambda --follow

πŸ› οΈ Troubleshooting

Issue Solution
Slack API not_authed Verify your bot token with curl -X POST https://slack.com/api/auth.test -H "Authorization: Bearer $SLACK_BOT_TOKEN"
"No partner available" Check the Azure AD group sync and the DynamoDB table contents
FreeBusy notFound errors The user's Google Calendar is not accessible. Ensure the service account has domain-wide delegation and the user has a Google Workspace account. Users whose calendars error are skipped as partners and logged as warnings
"Signature mismatch" Verify slack_signing_secret in the config secret
Timeout warnings in the channel The worker warns near the 15-minute Lambda cap and stops early β€” reduce the email list or meetings per person
"Permission denied" Check the Lambdas' IAM roles (Secrets Manager read, DynamoDB read/write, lambda:InvokeFunction for the UI role)

🀝 Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit changes (git commit -m 'feat: add amazing feature')
  4. Push to branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ”’ Security

Please see SECURITY.md for reporting vulnerabilities.


πŸ“„ License

This project is licensed under the MIT License β€” see the LICENSE file for details.


⭐ If this tool helped you, please give it a star! ⭐ · Made with ❀️ by Davide Caputo

About

🀝 Slack bot for automated 1:1 intro meetings. AWS Lambda + Google Calendar + Azure AD integration for team onboarding.

Topics

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages