DEV Community

Cover image for Create a Real-Time Brand Crisis Alert System (Reddit + Twitter)
Olamide Olaniyan
Olamide Olaniyan

Posted on

Create a Real-Time Brand Crisis Alert System (Reddit + Twitter)

A brand crisis doesn't start on the front page of the New York Times. It starts in a Reddit thread or a viral Tweet.

If you're managing a brand, finding out about a PR disaster 12 hours after it happens is too late. You need to know the moment negative sentiment spikes.

In this tutorial, we'll build a Node.js script that monitors Reddit and Twitter (X) for mentions of your brand, runs sentiment analysis on the posts, and sends a Slack/Discord webhook alert if a highly negative post starts gaining traction.

The Architecture

  1. Data Ingestion: Fetch recent posts mentioning the brand from Reddit and Twitter.
  2. Sentiment Analysis: Use a lightweight NLP library to score the text.
  3. Threshold Logic: Check if the sentiment is negative AND the engagement (upvotes/likes) is high.
  4. Alerting: Send a webhook to Slack.

Prerequisites

  • Node.js
  • A Slack Workspace (for the webhook URL)
  • SociaVault API Key (to fetch Reddit and Twitter data without dealing with expensive enterprise API tiers).
npm install axios sentiment dotenv
Enter fullscreen mode Exit fullscreen mode

Step 1: Fetching the Data

We'll use SociaVault to search both platforms.

require('dotenv').config();
const axios = require('axios');
const Sentiment = require('sentiment');

const API_KEY = process.env.SOCIAVAULT_API_KEY;
const BASE_URL = 'https://api.sociavault.com/v1';
const sentiment = new Sentiment();

async function fetchMentions(brandName) {
  const headers = { 'Authorization': `Bearer ${API_KEY}` };
  let mentions = [];

  try {
    // 1. Fetch Reddit Mentions
    const redditRes = await axios.get(`${BASE_URL}/reddit/search`, {
      headers, params: { query: brandName, sort: 'new', limit: 10 }
    });

    const redditPosts = redditRes.data.data.map(post => ({
      platform: 'Reddit',
      text: `${post.title} ${post.selftext}`,
      url: `https://reddit.com${post.permalink}`,
      engagement: post.score,
      author: post.author
    }));

    mentions.push(...redditPosts);

    // 2. Fetch Twitter Mentions
    const twitterRes = await axios.get(`${BASE_URL}/twitter/search/recent`, {
      headers, params: { query: brandName, limit: 10 }
    });

    const tweets = twitterRes.data.data.map(tweet => ({
      platform: 'Twitter',
      text: tweet.text,
      url: `https://twitter.com/user/status/${tweet.id}`,
      engagement: tweet.public_metrics.retweet_count + tweet.public_metrics.like_count,
      author: tweet.author_id
    }));

    mentions.push(...tweets);

  } catch (error) {
    console.error("Error fetching data:", error.message);
  }

  return mentions;
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Analyzing Sentiment

We'll use the sentiment npm package. It assigns a score to text. Negative numbers mean negative sentiment (e.g., -5 is very bad).

function analyzeMentions(mentions) {
  const alerts = [];

  mentions.forEach(mention => {
    const result = sentiment.analyze(mention.text);

    // CRISIS CRITERIA:
    // 1. Sentiment score is highly negative (<= -3)
    // 2. Engagement is gaining traction (> 50 likes/upvotes)
    if (result.score <= -3 && mention.engagement > 50) {
      alerts.push({
        ...mention,
        sentimentScore: result.score,
        negativeWords: result.negative
      });
    }
  });

  return alerts;
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Sending the Slack Alert

If our criteria are met, we fire off a webhook to Slack.

async function sendSlackAlert(alerts, webhookUrl) {
  if (alerts.length === 0) return;

  for (const alert of alerts) {
    const payload = {
      blocks: [
        {
          type: "header",
          text: {
            type: "plain_text",
            text: `🚨 BRAND CRISIS ALERT: ${alert.platform}`,
            emoji: true
          }
        },
        {
          type: "section",
          text: {
            type: "mrkdwn",
            text: `*Negative sentiment detected with high engagement!*\n\n*Text:* "${alert.text.substring(0, 150)}..."\n\n*Engagement:* ${alert.engagement}\n*Sentiment Score:* ${alert.sentimentScore}\n*Trigger Words:* ${alert.negativeWords.join(', ')}`
          }
        },
        {
          type: "section",
          text: {
            type: "mrkdwn",
            text: `<${alert.url}|View Post>`
          }
        }
      ]
    };

    try {
      await axios.post(webhookUrl, payload);
      console.log(`Alert sent for ${alert.platform} post.`);
    } catch (e) {
      console.error("Failed to send Slack alert");
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: The Cron Job

Wrap it in a function and run it on an interval (e.g., every 15 minutes).

const BRAND_NAME = "YourBrandName";
const SLACK_WEBHOOK = process.env.SLACK_WEBHOOK_URL;

async function runMonitor() {
  console.log(`[${new Date().toISOString()}] Running crisis monitor for ${BRAND_NAME}...`);

  const mentions = await fetchMentions(BRAND_NAME);
  const alerts = analyzeMentions(mentions);

  if (alerts.length > 0) {
    console.log(`Found ${alerts.length} potential crisis posts! Sending alerts...`);
    await sendSlackAlert(alerts, SLACK_WEBHOOK);
  } else {
    console.log("All clear. No crisis detected.");
  }
}

// Run immediately, then every 15 minutes
runMonitor();
setInterval(runMonitor, 15 * 60 * 1000);
Enter fullscreen mode Exit fullscreen mode

Why This is Powerful

By combining Reddit and Twitter, you cover the two platforms where viral outrage usually begins.

Historically, building this required paying $5,000+/month for Twitter Enterprise API access and dealing with Reddit's strict rate limits.

By using SociaVault, you get a unified, affordable API that handles the scraping, proxies, and rate limits for you. You just write the business logic.

Get your free API key at SociaVault.com and protect your brand today.

Top comments (0)