Skip to content

Repository files navigation

SecondSpace AI — Slack Bot

Meet the colleague you didn't know you had something in common with.

SecondSpace AI is a Slack bot that introduces people in a workspace who share interests, then does the awkward part for them: it reads both calendars, finds a time you are genuinely both free, and books the coffee chat.


What it does

🧠 Understands interests AI, A.I. and artificial intelligence are one thing. So are hiking and hiking trails, jazz and jazz music.
🤝 Ranks matches Scores every colleague on shared interests, and quietly deprioritises people you were introduced to recently so you keep meeting new faces.
📅 Finds real times Queries both Google Calendars for free/busy, respects working hours, timezones and daylight saving, and offers times spread across different days.
Books the meeting Creates the calendar event with a Google Meet link and invites you both.
🏠 Has an App Home Interests, calendar connection status and upcoming chats, all in one tab.
🧪 Runs without Google With no Google credentials configured it runs in demo mode against a simulated calendar, so the whole flow works on day one.

How it works

  1. Set your interests/interests hiking, AI, jazz (or run /interests for a form).
  2. Find a match/find_match shows the best candidate, what you have in common, and a match strength. Don't like it? Show someone else.
  3. Invite them — they get a DM with the shared interests and Accept / Decline.
  4. Connect calendars — each of you authorises read-only free/busy access, once.
  5. Pick a time — the bot offers a few slots you are both free for; one click books it.
sequenceDiagram
    actor Alice
    participant Bot as SecondSpace
    actor Bob
    participant GCal as Google Calendar

    Alice->>Bot: /interests hiking, AI, jazz
    Bot-->>Alice: Saved. Find someone to meet?
    Alice->>Bot: /find_match
    Bot-->>Alice: Meet @Bob — you both mentioned jazz (43%)
    Alice->>Bot: Invite to a coffee chat
    Bot-->>Bob: @Alice would like a coffee chat. Sounds good / Not this time
    Bob->>Bot: Sounds good
    Bot-->>Bob: Connect your calendar (once)
    Bob->>GCal: OAuth consent
    GCal-->>Bot: Refresh token
    Bot->>GCal: freebusy(Alice, Bob, next 7 days)
    GCal-->>Bot: Busy blocks
    Bot-->>Bob: Tue 10:00 · Wed 14:30 · Thu 09:30
    Bob->>Bot: Wed 14:30
    Bot->>GCal: Create event + Meet link, invite both
    GCal-->>Bot: Event link
    Bot-->>Alice: ✅ Coffee chat booked with @Bob
    Bot-->>Bob: ✅ Coffee chat booked with @Alice
Loading

Quick start (demo mode, no Google account needed)

git clone <this repo> && cd SecondSpaceAI
python -m venv .venv && source .venv/Scripts/activate   # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env                                    # add your Slack credentials
python app.py

In another terminal, expose the port so Slack can reach you:

ngrok http 3000

Put the ngrok URL in PUBLIC_BASE_URL in .env, restart, and point your Slack app at https://<your-ngrok-url>/slack/events.

With no GOOGLE_CLIENT_ID set, the bot runs in demo mode: "connecting a calendar" is a one-click local page, and each user gets a plausible simulated busy calendar. Every other part of the flow — matching, slot finding, booking, messaging — is real.

python app.py --check prints the resolved configuration without starting the server.


Full setup

1. Create the Slack app

The fastest route is the included manifest:

  1. Go to https://api.slack.com/appsCreate New AppFrom an app manifest.
  2. Paste slack-app-manifest.yaml, replacing every REPLACE-ME with your public hostname.
  3. Install to Workspace, then copy into .env:
    • Bot User OAuth Token (xoxb-…) → SLACK_BOT_TOKEN
    • Signing SecretSLACK_SIGNING_SECRET

The manifest requests these bot scopes:

Scope Why
commands /interests and /find_match
chat:write Post match cards, invitations and confirmations
im:write Open a DM with the person being invited
im:history Reply with help when someone DMs the bot
users:read Display names and timezones, so times render in local time
users:read.email Add both people as guests on the calendar event
app_mentions:read Respond to @-mentions

Everything arrives at a single request URL: <PUBLIC_BASE_URL>/slack/events.

2. (Optional) Enable real Google Calendar

  1. Google Cloud Console → create a project.
  2. APIs & Services → Library → enable Google Calendar API.
  3. OAuth consent screenInternal (or add your testers as test users).
  4. Credentials → Create Credentials → OAuth client ID → Web application.
  5. Add the authorised redirect URI: <PUBLIC_BASE_URL>/google/oauth/callback
  6. Put the client ID and secret in .env (or download the JSON to credentials.json).

The bot requests only two scopes: calendar.freebusy (when you are busy — not what you are doing) and calendar.events (to create the coffee chat).

3. Protect stored tokens

Refresh tokens are long-lived. Set TOKEN_ENCRYPTION_KEY and they are encrypted at rest with Fernet:

python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

Commands

Command What it does
/interests hiking, AI, jazz Save your interests
/interests Open a form to edit them
/interests show See what's on file
/interests clear Forget your interests (removes you from matching)
/interests help Show help
/find_match Find someone with overlapping interests
/find_match cooking, cycling Match on a one-off topic instead of your saved interests

Configuration

Everything is set through the environment or .env — see .env.example for the annotated list.

Variable Default Notes
SLACK_BOT_TOKEN Required
SLACK_SIGNING_SECRET Required; proves requests came from Slack
PUBLIC_BASE_URL http://localhost:$PORT Where Google redirects back to
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET Blank ⇒ demo mode
DATABASE_PATH secondspace.db SQLite file
TOKEN_ENCRYPTION_KEY Fernet key; blank ⇒ tokens stored as plaintext
DEFAULT_TIMEZONE America/New_York Fallback when Slack has no timezone for a user
WORKING_HOURS_START / _END 9 / 17 Local to each meeting's organiser
MEETING_DURATION_MINUTES 30
SCHEDULING_HORIZON_DAYS 7 How far ahead to look
MIN_LEAD_TIME_MINUTES 120 Never book something in ten minutes' time
MAX_SLOT_OPTIONS 3 How many times to offer
INCLUDE_WEEKENDS false
REMATCH_COOLDOWN_DAYS 30 How long before the same pair is suggested again

How it decides who should meet

/interests hiking, A.I., Board Games becomes the canonical tags hiking, artificial intelligence, board game — lowercased, depluralised, filler words dropped, and common aliases collapsed (MLmachine learning, bikingcycling).

Two people are then compared on two signals, each measured as the average of Jaccard similarity and the overlap coefficient:

  • tag overlap — whole interests in common, where a phrase that contains another (jazz inside jazz music) counts for 85% of an exact match;
  • token overlap — the individual words, so trail running and running club still find each other.

The final score is 0.6 × tags + 0.4 × tokens. Averaging Jaccard with the overlap coefficient matters: pure Jaccard punishes the colleague who listed twenty interests, which is exactly the person you probably want to meet.

Anyone you were shown or met recently is multiplied by a penalty (0.6 and 0.25) rather than hidden — in a five-person workspace they may be the only option.

How it picks a time

  1. Build a window: now + MIN_LEAD_TIME through now + SCHEDULING_HORIZON_DAYS.
  2. Ask Google for both people's busy blocks in that window and merge them.
  3. Walk every candidate slot inside local working hours, skipping weekends and anything that collides with a busy block. Slots are generated in local wall time and converted to UTC, so daylight saving transitions land correctly.
  4. Offer up to MAX_SLOT_OPTIONS, round-robined across different days — "Tue 10:00, Wed 14:30, Thu 09:30" is a better menu than three slots in a row.

Project layout

app.py                          entry point (python app.py [--check])
secondspace/
    config.py                   environment → typed Config
    db.py                       SQLite: users, interests, tokens, meetings
    matching.py                 interest normalisation + similarity scoring   (pure)
    scheduling.py               free/busy → candidate slots                   (pure)
    calendar_integration.py     Google provider + simulated provider
    services.py                 orchestration: match → invite → schedule → book
    blocks.py                   Block Kit builders
    slack_app.py                Bolt listeners (commands, buttons, modal, home)
    web.py                      Flask routes: /slack/events, OAuth redirect, /healthz
tests/                          full test suite, no network access required
slack-app-manifest.yaml         one-paste Slack app setup

The two modules that decide anything interesting — matching and scheduling — are pure functions with no clock, database or network, which is what makes them straightforward to test and to tune.


Tests

pip install -r requirements.txt
python -m pytest

The suite covers normalisation and scoring, slot finding across a daylight saving transition, the SQLite layer, the simulated calendar, Block Kit output against Slack's structural limits, and the Slack listeners themselves — real block_actions, slash command and view_submission payloads dispatched through a real Bolt app with the Web API stubbed out. Nothing touches the network.


Notes on privacy

  • The bot stores Slack user IDs, display names, emails, timezones and the interests people give it — nothing else from your workspace.
  • Calendar access is free/busy only: it can see that you are booked from 2–3pm, not what the meeting is.
  • /interests clear removes someone from matching entirely, and the App Home has a Disconnect button that deletes the stored calendar token.

Ideas worth building next

  • Group coffee chats for three or four people around a shared interest
  • Icebreaker prompts generated from the actual overlap
  • A weekly opt-in "introduce me to someone" digest
  • Feedback after the chat, to learn which matches actually landed
  • Channel-scoped matching (/find_match #design)

PRs and issues welcome.

About

Building an AI agent for connectivity at the workplace!

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages