Skip to content

feat: Slack notification on new user signup#5

Merged
jeremylongshore merged 3 commits intomainfrom
feat/slack-new-user-notification
Feb 13, 2026
Merged

feat: Slack notification on new user signup#5
jeremylongshore merged 3 commits intomainfrom
feat/slack-new-user-notification

Conversation

@jeremylongshore
Copy link
Collaborator

@jeremylongshore jeremylongshore commented Feb 13, 2026

Summary

  • Adds Firebase Cloud Function (onNewUser) that fires on every Firebase Auth signup
  • Posts to #operation-hired Slack channel with user email, UID, provider, and timestamp
  • Uses SLACK_WEBHOOK_URL Firebase secret (already configured via firebase functions:secrets:set)
  • Adds functions/ directory with firebase-functions v1 Auth trigger

Files added

  • functions/index.js — Cloud Function with Auth onCreate trigger
  • functions/package.json — Dependencies (firebase-admin, firebase-functions)
  • functions/.gitignore — Excludes node_modules
  • firebase.json — Updated to include functions config

Deploy steps (after merge)

firebase deploy --only functions --project perception-with-intent

Test plan

  • Deploy function to Firebase
  • Create a test account on https://perception-with-intent.web.app
  • Verify Slack message appears in #operation-hired with correct user info
  • Check Firebase Functions logs for successful execution

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Real-time Slack notifications for new user sign-ups.
  • Chores

    • Project planning updated with new epics/tasks for ingestion automation, observability, Slack notifications, and content/brief generation.
    • Cloud Functions support added to deployment configuration; function packaging and dependencies configured and node_modules now ignored.
    • Internal tracking state updated to reflect the new notification work item.

Add Firebase Cloud Function (Auth onCreate trigger) that posts to
#operation-hired Slack channel when a new user signs up on Perception.

Uses SLACK_WEBHOOK_URL Firebase secret (already configured).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link

Summary of Changes

Hello @jeremylongshore, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request implements a new feature to enhance real-time operational awareness by automatically notifying a designated Slack channel whenever a new user registers. This is achieved by deploying a Firebase Cloud Function that listens for new user signups and then securely dispatches relevant user details to Slack, ensuring the team is immediately informed of new platform adoption.

Highlights

  • New Firebase Cloud Function: Introduced an onNewUser Firebase Cloud Function that triggers upon every new Firebase Authentication signup.
  • Slack Notification Integration: Configured the Cloud Function to post detailed user signup information (email, UID, provider, timestamp) to the #operation-hired Slack channel.
  • Secret Management: Utilized the SLACK_WEBHOOK_URL Firebase secret for secure Slack webhook integration, which is already pre-configured.
  • Project Structure Update: Added a new functions/ directory containing the Cloud Function logic and its dependencies, along with updating firebase.json to include the functions configuration.
Changelog
  • firebase.json
    • Added configuration for Firebase Functions, specifying the 'functions' directory as the source and 'nodejs22' as the runtime.
  • functions/.gitignore
    • Added node_modules/ to prevent unnecessary files from being committed to the repository.
  • functions/index.js
    • Created a new Firebase Cloud Function named onNewUser that triggers on Firebase Auth user creation.
    • Implemented logic to construct a Slack message payload with new user details.
    • Added an HTTPS request to send the formatted message to the configured Slack webhook URL.
    • Included error handling and logging for successful notifications or failures.
  • functions/package.json
    • Added firebase-admin and firebase-functions as dependencies for the new Cloud Function.
    • Configured the Node.js engine version to '22' for the functions environment.
Activity
  • No specific activity (comments, reviews, or progress updates) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Feb 13, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds a Firebase Functions configuration and a Cloud Function (Node.js 22) that triggers on Auth user creation to post masked signup details to a Slack webhook; also adds functions package manifest and .gitignore entry, and updates project issue tracking with new epics/tasks for ingestion, observability, and notifications.

Changes

Cohort / File(s) Summary
Firebase config
firebase.json
Adds a functions block: source: "functions", runtime: "nodejs22".
Cloud Functions code & manifest
functions/index.js, functions/package.json, functions/.gitignore
Adds exports.onNewUser — an Auth onCreate Cloud Function that masks email and posts signup info to Slack using secret SLACK_WEBHOOK_URL, with timeout and error handling; adds Node 22 manifest and ignores node_modules/.
Project issues / planning
.beads/issues.jsonl, .beads/last-touched
Adds many new epics/tasks for ingestion automation, observability (OpenTelemetry/Cloud Trace), Slack notification rollout, and content/brief generation; updates dependencies and marks last-touched entry to perception-s9t.1.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Auth as Firebase Auth
    participant CF as Cloud Function
    participant Slack as Slack Webhook

    User->>Auth: Sign up / create account
    Auth->>CF: onCreate trigger (user data)
    CF->>CF: Mask email, build payload, read secret
    CF->>Slack: POST payload to webhook (with timeout)
    Slack-->>CF: HTTP 200 / error response
    CF-->>CF: Log success or error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐇 I hopped into code with a tiny cheer,
A webhook tucked close so Slack can hear,
New users arrive, I mask and relay,
Node and Firebase send news on their way,
I nibble logs and scamper off — hooray! 🥕✨

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the main feature: adding Slack notifications for new user signups, which matches the primary changes in the changeset.
Description check ✅ Passed The PR description covers the summary of changes, files added, deployment steps, and test plan. However, it lacks specific testing checkboxes marked as complete.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/slack-new-user-notification

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a Firebase function to send a Slack notification on new user signups. The implementation is sound, but I've identified a couple of areas for improvement in functions/index.js. Specifically, I've suggested removing some unused code related to v2 functions and refactoring the HTTP request logic to use the modern fetch API, which is available in the nodejs22 runtime. This will make the code cleaner, more robust, and easier to maintain. The rest of the changes look good.

Comment on lines +18 to +71
const https = require("https");

exports.onNewUser = functions
.runWith({ secrets: ["SLACK_WEBHOOK_URL"] })
.auth.user()
.onCreate(async (user) => {
const webhookUrl = process.env.SLACK_WEBHOOK_URL;
if (!webhookUrl) {
logger.error("SLACK_WEBHOOK_URL secret not configured");
return;
}

const createdAt = user.metadata.creationTime || new Date().toISOString();
const payload = JSON.stringify({
text: [
":new: *New Perception user signup*",
`*Email:* ${user.email || "no email"}`,
`*UID:* \`${user.uid}\``,
`*Provider:* ${user.providerData?.[0]?.providerId || "email"}`,
`*Time:* ${createdAt}`,
].join("\n"),
});

return new Promise((resolve, reject) => {
const url = new URL(webhookUrl);
const req = https.request(
{
hostname: url.hostname,
path: url.pathname,
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(payload),
},
},
(res) => {
let data = "";
res.on("data", (chunk) => (data += chunk));
res.on("end", () => {
if (res.statusCode === 200) {
logger.info(`Slack notified for new user: ${user.email}`);
resolve(data);
} else {
logger.error(`Slack webhook failed: ${res.statusCode} ${data}`);
reject(new Error(`Slack webhook failed: ${res.statusCode}`));
}
});
}
);
req.on("error", reject);
req.write(payload);
req.end();
});
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation for sending the Slack notification uses the native https module wrapped in a Promise. This is verbose and contains an anti-pattern where an async function returns a new Promise. Since the function runtime is nodejs22, we can leverage the built-in fetch API to greatly simplify this logic, make it more robust, and align with modern JavaScript practices. This change also makes the https module import unnecessary.

exports.onNewUser = functions
  .runWith({ secrets: ["SLACK_WEBHOOK_URL"] })
  .auth.user()
  .onCreate(async (user) => {
    const webhookUrl = process.env.SLACK_WEBHOOK_URL;
    if (!webhookUrl) {
      logger.error("SLACK_WEBHOOK_URL secret not configured");
      return;
    }

    const createdAt = user.metadata.creationTime || new Date().toISOString();
    const payload = {
      text: [
        ":new: *New Perception user signup*",
        `*Email:* ${user.email || "no email"}`,
        `*UID:* \`${user.uid}\``,
        `*Provider:* ${user.providerData?.[0]?.providerId || "email"}`,
        `*Time:* ${createdAt}`,
      ].join("\n"),
    };

    try {
      const response = await fetch(webhookUrl, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(payload),
      });

      if (response.ok) {
        logger.info(`Slack notified for new user: ${user.email}`);
      } else {
        const errorText = await response.text();
        logger.error(
          `Slack webhook failed: ${response.status} ${response.statusText}`,
          { error: errorText }
        );
      }
    } catch (error) {
      logger.error("Error sending Slack notification", { error });
    }
  });

Comment on lines +1 to +5
const { onDocumentCreated } = require("firebase-functions/v2/firestore");
const { defineSecret } = require("firebase-functions/params");
const { logger } = require("firebase-functions");

const slackWebhookUrl = defineSecret("SLACK_WEBHOOK_URL");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These imports and variable definitions appear to be from an attempt to use v2 Cloud Functions. Since the implementation uses a v1 Auth trigger, onDocumentCreated, defineSecret, and the slackWebhookUrl constant are unused. They should be removed to improve clarity and reduce code clutter.

Suggested change
const { onDocumentCreated } = require("firebase-functions/v2/firestore");
const { defineSecret } = require("firebase-functions/params");
const { logger } = require("firebase-functions");
const slackWebhookUrl = defineSecret("SLACK_WEBHOOK_URL");
const { logger } = require("firebase-functions");

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@functions/index.js`:
- Around line 31-38: The Slack payload construction (variable payload in
functions/index.js) currently embeds PII (user.email and user.uid) into the
message and function logs; remove or mask these values before sending or
logging—replace user.email with a non-PII placeholder or masked string (e.g.,
omit domain or use "<redacted>") and avoid printing raw user.uid (use a
truncated/hash form) wherever payload is built or logged (also apply the same
change to the other payload/log locations around the code noted at lines 58-59).
Ensure any processLogger or console.log calls do not emit user.email or user.uid
and update the code paths that createPayload/sendSlackNotification to accept
already-redacted identity fields.
- Around line 41-70: The Slack webhook Promise currently creates an
https.request without a timeout, which can hang; add a timeout on the request
(use the returned request object `req`) — call `req.setTimeout(timeoutMs, ...)`
or attach a `'timeout'` handler to abort the request and reject the Promise with
a clear timeout Error, ensure you also remove/avoid double-rejects by cleaning
up listeners and calling `req.destroy()`/`req.abort()` as needed; reference the
existing symbols `webhookUrl`, `https.request`, `req`, `payload`, and `logger`
so the change is applied inside the same Promise block that resolves/rejects
based on `res.statusCode`.

In `@functions/package.json`:
- Around line 4-10: The package.json currently lists incompatible dependency
versions: "firebase-functions": "^6.3.0" and "firebase-admin": "^13.0.0"; update
the dependency pair so peer constraints match — either downgrade
"firebase-admin" to a ^12.x version or (preferred) bump "firebase-functions" to
a v7-compatible range (e.g. "^7.0.0") so it supports "firebase-admin@13"; edit
the "dependencies" section in package.json (look for the "firebase-functions"
and "firebase-admin" entries) to make this change and then run your install to
verify resolution.
🧹 Nitpick comments (2)
functions/index.js (2)

7-14: Doc comment doesn’t match behavior.

The comment describes a Firestore fallback, but only the Auth onCreate trigger is implemented. Update the comment or add the Firestore trigger to avoid confusion.

✏️ Suggested comment fix
- * Firebase Auth triggers aren't available in v2 yet for all runtimes,
- * so we watch the /users/{userId} Firestore collection instead.
- * If no Firestore doc is created on signup, we fall back to an Auth
- * onCreate trigger (v1).
+ * Firebase Auth triggers use the v1 API here, so we rely on the Auth
+ * onCreate trigger for signup notifications.

20-25: Remove unused defineSecret for clarity.

The slackWebhookUrl variable defined on line 5 is never used. The function binds the secret via runWith({ secrets: ["SLACK_WEBHOOK_URL"] }) and reads it via process.env.SLACK_WEBHOOK_URL on line 24, making the defineSecret call redundant. Remove it to avoid confusion:

Remove defineSecret
-const { defineSecret } = require("firebase-functions/params");
-const slackWebhookUrl = defineSecret("SLACK_WEBHOOK_URL");

- Replace https module with native fetch API (Node.js 22) with 10s timeout
- Mask PII in Slack payload and logs (email redacted, UID truncated to 8 chars)
- Remove unused v2 imports (defineSecret, onDocumentCreated, slackWebhookUrl)
- Fix misleading doc comment about Firestore fallback
- Downgrade firebase-admin to ^12.7.0 for peer dep compatibility with functions ^6.3.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.beads/issues.jsonl:
- Around line 35-38: The issue description for perception-s9t.1 incorrectly says
"Firestore onCreate trigger" — update the description and acceptance criteria to
reference the Firebase Auth onCreate trigger used by PR `#5` (e.g., replace
"Firestore onCreate" with "Firebase Auth onCreate") so reviewers and deploy/test
tasks clearly target the Auth user-creation event; ensure the description
mentions PR `#5` and that acceptance verifies the Auth onCreate Cloud Function is
merged and triggers on new user signup.
🧹 Nitpick comments (2)
functions/package.json (1)

1-12: Consider adding "private": true to prevent accidental publishing.

Since this package is internal to your Firebase project, adding "private": true guards against accidental npm publish.

📦 Suggested addition
 {
   "name": "perception-functions",
   "description": "Firebase Cloud Functions for Perception",
+  "private": true,
   "engines": {
     "node": "22"
   },
functions/index.js (1)

1-10: Consolidate duplicate imports from the same module.

Both logger (line 1) and functions (line 10) are imported from firebase-functions. Consider combining them into a single import statement.

♻️ Suggested consolidation
-const { logger } = require("firebase-functions");
-
-/**
- * Notify Slack when a new user signs up.
- *
- * Uses the v1 Auth onCreate trigger which fires on every
- * Firebase Auth signup event.
- */
-
-const functions = require("firebase-functions");
+const functions = require("firebase-functions");
+const { logger } = functions;
+
+/**
+ * Notify Slack when a new user signs up.
+ *
+ * Uses the v1 Auth onCreate trigger which fires on every
+ * Firebase Auth signup event.
+ */

Change "Firestore onCreate trigger" to "Firebase Auth onCreate trigger"
to match the actual implementation in PR #5.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jeremylongshore jeremylongshore merged commit 2bf2d7c into main Feb 13, 2026
8 of 9 checks passed
jeremylongshore added a commit that referenced this pull request Feb 13, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jeremylongshore added a commit that referenced this pull request Feb 13, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jeremylongshore added a commit that referenced this pull request Feb 13, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jeremylongshore jeremylongshore deleted the feat/slack-new-user-notification branch February 13, 2026 14:07
jeremylongshore added a commit that referenced this pull request Feb 13, 2026
- perception-s9t.1: update description to past tense (PR #5 was merged)
- perception-s9t: clarify PR #5 already merged in epic description
- perception-pkm, perception-t89: refresh updated_at timestamps
- perception-a27.2: fix file path (routers/trigger.py not routers/rss.py)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jeremylongshore added a commit that referenced this pull request Feb 13, 2026
… content (#6)

* chore(beads): create 4 epics with tasks for ingestion, observability, notifications, and content

Set up structured task tracking with beads for identified issues:
- PERC-INGEST (P1): Automated daily ingestion pipeline (4 tasks, chained deps)
- PERC-OBSERVABILITY (P2): OpenTelemetry + Cloud Trace (3 tasks, chained)
- PERC-NOTIFY (P2): User signup Slack notifications (3 tasks, chained)
- PERC-CONTENT (P2): Brief & summary generation (3 tasks, cross-epic dep on INGEST)

Reparented existing tasks (generate_brief, store_articles) under PERC-CONTENT epic.
Installed updated git hooks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(beads): use absolute date and refresh stale updated_at timestamps

- Replace "7+ days stale" with "Last ingestion: 2026-02-06" in PERC-INGEST epic
- Bump updated_at on perception-pkm and perception-t89 after reparenting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(beads): update PERC-NOTIFY epic to reflect PR #5 merged

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(beads): sync JSONL after rebase and PR #5 merge

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(beads): address Gemini and CodeRabbit review comments

- perception-s9t.1: update description to past tense (PR #5 was merged)
- perception-s9t: clarify PR #5 already merged in epic description
- perception-pkm, perception-t89: refresh updated_at timestamps
- perception-a27.2: fix file path (routers/trigger.py not routers/rss.py)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: jeremylongshore <jeremylongshore@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant