Skip to content

feat(slack): DM the Slack user named by an alert label - #175

Open
blacksoxx wants to merge 3 commits into
FluidifyAI:mainfrom
blacksoxx:feat/dm-slack-user-by-email
Open

blacksoxx wants to merge 3 commits into
FluidifyAI:mainfrom
blacksoxx:feat/dm-slack-user-by-email

Conversation

@blacksoxx

@blacksoxx blacksoxx commented Oct 9, 2026 •

Copy link
Copy Markdown

Summary

A routing rule can name a label that holds an email. When the address belongs to an active Regen user, Regen sends that user a Slack DM. The DM still goes out when the rule suppresses the incident, so an alert can reach its owner without opening an incident or paging the on-call schedule.

Only Regen users can be reached. Addresses that do not match a user are ignored, so a webhook sender cannot DM arbitrary workspace members. Email matching is case-insensitive. When the user has a linked Slack ID the DM goes straight to that ID with no lookup; otherwise the address is resolved with users.lookupByEmail only, with no display-name fallback.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Documentation
  • CI / tooling

Testing

  • make test passes
  • Tested the affected integration manually (Slack / Teams / Telegram / AI / etc.)
  • New database migrations have a corresponding .down.sql
  • No hardcoded secrets, tokens, or credentials

Ran make test (backend with -race, frontend vitest) and golangci-lint run with 0 issues. Sent one real DM from a suppressed alert carrying an owner label to a Regen user with no linked Slack ID, so the users.lookupByEmail path was exercised.

Screenshots (if UI change)

Routing rule form has a "Slack DM from label" field.

Related issues

Closes #174

Routing rules can set notify_email_label to a label key. When that
label holds an email, Regen looks the address up in Slack and sends
a DM. The DM is sent even if the rule suppresses the incident.

Closes FluidifyAI#174
@singret

singret commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

Thanks for the PR, Youssef.

My main question is about scope. Regen is built around incidents and on-call, and this adds a path that DMs someone from an alert with no incident and no schedule involved. For the CI case, a GitHub/GitLab Slack integration or a small script on your side already does this. What does having it in the routing rules give you that those don't?

If we do keep it, a few things to sort out:

  • Could you confirm SendDirectMessage actually resolves by email through users.lookupByEmail? From the diff it looks like it may fall back to a display-name search, and I'd want to see that tested against the real Slack service.
  • Any webhook sender can make Regen DM any workspace member. Could we add a per-address rate limit or an allowlist of email domains?
  • Please run make test and try one real DM, then update the checklist.

Happy to talk it through if you think I'm missing the use case.

@blacksoxx

Copy link
Copy Markdown
Author

Fair question. The case I had was any alert that carries an owner email label, not only CI. The alert is already in Regen with Slack set up, so the rule sits next to the other actions instead of in a separate script.

If that is not where you want Regen to go, I am happy to close this. If you want to keep it, I would restrict the DM to addresses that match an existing Regen user, confirm the lookup goes through users.lookupByEmail only, run make test and a real DM, and update the checklist.

@singret

singret commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

That makes sense, owner routing is a better framing than CI. Let's keep it with your changes: restrict DMs to emails matching an existing Regen user, users.lookupByEmail only, make test plus one real DM, and update the checklist. Ping me when it's ready for another look.

The label address must match an active Regen user. When that user has
a linked Slack ID the DM goes straight to it. Otherwise the address is
resolved with users.lookupByEmail only; the display-name fallback in
SendDirectMessage is not used for this path.

Signed-off-by: Youssef Omar Bouden <youssef.bouden2002@gmail.com>
@singret

singret commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Thanks, the new commit does what we discussed. Before merge:

  • If a user already has a saved Slack ID, DM that ID directly. Right now IDs that don't start with U (like W… on Enterprise Grid) get treated as a name and searched by display name. The check is here:
    // Slack user IDs look like U0AJLLY3678 — skip expensive lookup when we already have one.
    if strings.HasPrefix(username, "U") && len(username) >= 9 {
    userID = username
    } else {
    var err error
    userID, err = s.lookupUserID(username)
    if err != nil {
    return fmt.Errorf("failed to look up user %q: %w", username, err)
    }
    }
    (strings.HasPrefix(username, "U"), anything else goes to lookupUserID, which falls back to a display-name scan).
  • Match emails case-insensitively. Dev@Example.com currently won't find dev@example.com.
  • Send one real DM, tick the checklist, and update the description to say "Regen user".
  • Add a line to the routing docs: the sender controls the DM text, so the webhook should be authenticated and the rule narrow.

We can look at rate limiting separately.

…-insensitively

A stored Slack ID is sent to the DM method that skips every lookup, so
IDs that do not start with U (Enterprise Grid W IDs) no longer fall into
the display-name scan. The label email is lower-cased before the user
lookup and GetByEmail compares with LOWER() on both sides. The routing
doc notes that the webhook sender controls the DM text.

Signed-off-by: Youssef Omar Bouden <youssef.bouden2002@gmail.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DM the Slack user named by an alert label

2 participants