← Monday's Prompts

Automate Crisis Response šŸš€

Turn Monday's 3 prompts into instant multi-channel publishing

October 21, 2025
šŸ‘” Leadership⚔ TypeScript + PythonšŸ“¢ Multi-Channel Publishing

The Problem

On Monday you tested the 3 prompts in ChatGPT. You drafted a statement, tailored it for different audiences, and planned follow-ups. That's great for one crisis. But here's reality: when you're dealing with a data breach at 2am, you can't spend 2 hours copy-pasting messages across 6 channels. By the time you're done, Twitter's already on fire and your customers are panicking.

2-4 hours
Per crisis spent manually posting
35% error
From inconsistent messaging across channels
Can't scale
Beyond 5-10 crises per year

See It Work

Watch the 3 prompts chain together automatically. This is what you'll build - from crisis detection to multi-channel publishing in 30 seconds.

The Code

Three levels: start simple, add approval workflows, then scale to real-time monitoring. Pick where you are.

Level 1: Simple Multi-Channel Publishing

Good for: 1-5 crises/year | Setup time: 30 minutes

// Simple Multi-Channel Publishing (1-5 crises/year)
import Anthropic from '@anthropic-ai/sdk';
import Twitter from 'twitter-api-v2';
import { Client as LinkedInClient } from 'linkedin-api-client';
import sgMail from '@sendgrid/mail';
import twilio from 'twilio';

interface CrisisMessage {
  twitter: string;
  linkedin: string;
  email_subject: string;
  email_body: string;
  sms: string;
}

async function generateCrisisMessages(
  crisisDescription: string
): Promise<CrisisMessage> {
  const anthropic = new Anthropic({
    apiKey: process.env.ANTHROPIC_API_KEY!,
  });

  const prompt = `You are a crisis communication expert. Given this crisis situation, generate appropriate messages for different channels.

Crisis: ${crisisDescription}

Generate messages for:
1. Twitter (under 280 chars, direct and factual)
2. LinkedIn (professional, detailed, 200-300 words)
3. Email subject line (clear, urgent, under 60 chars)
4. Email body (detailed, empathetic, actionable)
5. SMS (under 160 chars, essential info only)

Output as JSON with keys: twitter, linkedin, email_subject, email_body, sms

Tone: Transparent, factual, empathetic. No corporate jargon.`;

  const response = await anthropic.messages.create({
    model: 'claude-3-5-sonnet-20241022',
    max_tokens: 2000,
    messages: [{ role: 'user', content: prompt }],
  });

  const content = response.content[0];
  if (content.type !== 'text') throw new Error('Invalid response');

  return JSON.parse(content.text);
}

async function publishToAllChannels(messages: CrisisMessage) {
  // Twitter
  const twitterClient = new Twitter({
    appKey: process.env.TWITTER_API_KEY!,
    appSecret: process.env.TWITTER_API_SECRET!,
    accessToken: process.env.TWITTER_ACCESS_TOKEN!,
    accessSecret: process.env.TWITTER_ACCESS_SECRET!,
  });

  const tweet = await twitterClient.v2.tweet(messages.twitter);
  console.log(`Published to Twitter: ${tweet.data.id}`);

  // LinkedIn
  const linkedInClient = new LinkedInClient({
    clientId: process.env.LINKEDIN_CLIENT_ID!,
    clientSecret: process.env.LINKEDIN_CLIENT_SECRET!,
    redirectUrl: process.env.LINKEDIN_REDIRECT_URL!,
  });

  const linkedInPost = await linkedInClient.createShare({
    owner: `urn:li:person:${process.env.LINKEDIN_PERSON_ID}`,
    text: messages.linkedin,
  });
  console.log(`Published to LinkedIn: ${linkedInPost.id}`);

  // Email (SendGrid)
  sgMail.setApiKey(process.env.SENDGRID_API_KEY!);

  const affectedCustomers = await getAffectedCustomers(); // Your DB query

  await sgMail.sendMultiple({
    to: affectedCustomers.map((c) => c.email),
    from: 'security@company.com',
    subject: messages.email_subject,
    text: messages.email_body,
    html: messages.email_body.replace(/\n/g, '<br>'),
  });
  console.log(`Sent ${affectedCustomers.length} emails`);

  // SMS (Twilio)
  const twilioClient = twilio(
    process.env.TWILIO_ACCOUNT_SID!,
    process.env.TWILIO_AUTH_TOKEN!
  );

  const smsPromises = affectedCustomers.map((customer) =>
    twilioClient.messages.create({
      body: messages.sms,
      from: process.env.TWILIO_PHONE_NUMBER!,
      to: customer.phone,
    })
  );

  await Promise.all(smsPromises);
  console.log(`Sent ${affectedCustomers.length} SMS messages`);
}

// Usage
const crisisDescription = `Payment system breach, 12K cards exposed...`;
const messages = await generateCrisisMessages(crisisDescription);
await publishToAllChannels(messages);

// Helper function (implement based on your DB)
async function getAffectedCustomers() {
  // Query your database for affected customers
  return [
    { email: 'customer1@example.com', phone: '+1234567890' },
    // ... more customers
  ];
}

Level 2: With Approval Workflow & Validation

Good for: 5-20 crises/year | Setup time: 2 hours

# With Approval Workflow & Validation (5-20 crises/year)
import anthropic
import asyncio
from typing import Dict, List
import smtplib
from email.mime.text import MIMEText
from dataclasses import dataclass
import json

@dataclass
class Approver:
    role: str
    email: str
    required: bool
    approved: bool = False

class CrisisResponseSystem:
    def __init__(self):
        self.anthropic = anthropic.Anthropic(
            api_key=os.environ.get("ANTHROPIC_API_KEY")
        )
        self.pending_approvals = {}
    
    async def generate_crisis_response(self, crisis_description: str) -> Dict:
        """Generate messages and determine approval requirements"""
        
        # Step 1: Analyze crisis severity
        severity_prompt = f"""Analyze this crisis and determine:
1. Severity level (low/medium/high/critical)
2. Required approvers (CEO, Legal, PR, etc.)
3. Legal review needed? (yes/no)
4. Immediate publication recommended? (yes/no)

Crisis: {crisis_description}

Output as JSON."""
        
        severity_response = self.anthropic.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=1024,
            messages=[{"role": "user", "content": severity_prompt}]
        )
        
        severity_data = json.loads(severity_response.content[0].text)
        
        # Step 2: Generate messages
        message_prompt = f"""Generate crisis communication messages.

Crisis: {crisis_description}
Severity: {severity_data['severity']}

Generate:
1. Twitter (under 280 chars)
2. LinkedIn (200-300 words, professional)
3. Email subject + body (detailed, actionable)
4. SMS (under 160 chars)

Output as JSON with keys: twitter, linkedin, email_subject, email_body, sms"""
        
        message_response = self.anthropic.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=2000,
            messages=[{"role": "user", "content": message_prompt}]
        )
        
        messages = json.loads(message_response.content[0].text)
        
        return {
            "severity": severity_data,
            "messages": messages,
            "approval_id": self._create_approval_request(severity_data, messages)
        }
    
    def _create_approval_request(self, severity: Dict, messages: Dict) -> str:
        """Create approval request and notify approvers"""
        approval_id = f"crisis_{int(time.time())}"
        
        # Determine required approvers based on severity
        approvers = []
        if severity['severity'] in ['high', 'critical']:
            approvers.append(Approver('CEO', 'ceo@company.com', True))
            approvers.append(Approver('General Counsel', 'legal@company.com', True))
        
        if severity['legal_review_needed']:
            approvers.append(Approver('Legal Team', 'legal@company.com', True))
        
        approvers.append(Approver('Head of PR', 'pr@company.com', False))
        
        self.pending_approvals[approval_id] = {
            'approvers': approvers,
            'messages': messages,
            'created_at': time.time(),
            'status': 'pending'
        }
        
        # Send approval requests via email
        self._send_approval_emails(approval_id, approvers, messages)
        
        return approval_id
    
    def _send_approval_emails(self, approval_id: str, approvers: List[Approver], messages: Dict):
        """Send approval request emails with preview"""
        smtp_server = smtplib.SMTP('smtp.gmail.com', 587)
        smtp_server.starttls()
        smtp_server.login(
            os.environ.get('EMAIL_USER'),
            os.environ.get('EMAIL_PASSWORD')
        )
        
        for approver in approvers:
            email_body = f"""URGENT: Crisis Communication Approval Required

Role: {approver.role}
Required: {'YES' if approver.required else 'NO'}

Proposed Messages:

Twitter:
{messages['twitter']}

LinkedIn:
{messages['linkedin']}

Email Subject: {messages['email_subject']}

Approve: https://company.com/approve/{approval_id}?approver={approver.email}
Reject: https://company.com/reject/{approval_id}?approver={approver.email}

This requires immediate attention."""
            
            msg = MIMEText(email_body)
            msg['Subject'] = f'URGENT: Crisis Communication Approval - {approval_id}'
            msg['From'] = 'crisis-system@company.com'
            msg['To'] = approver.email
            
            smtp_server.send_message(msg)
        
        smtp_server.quit()
    
    async def check_approval_status(self, approval_id: str) -> Dict:
        """Check if all required approvals received"""
        approval_data = self.pending_approvals.get(approval_id)
        if not approval_data:
            return {'status': 'not_found'}
        
        required_approvers = [a for a in approval_data['approvers'] if a.required]
        all_approved = all(a.approved for a in required_approvers)
        
        if all_approved:
            # Auto-publish if all required approvals received
            await self._publish_messages(approval_data['messages'])
            approval_data['status'] = 'published'
            return {'status': 'published', 'published_at': time.time()}
        
        return {
            'status': 'pending',
            'approvals': [
                {'role': a.role, 'approved': a.approved, 'required': a.required}
                for a in approval_data['approvers']
            ]
        }
    
    async def _publish_messages(self, messages: Dict):
        """Publish to all channels"""
        # Implementation similar to Level 1 example
        # Twitter, LinkedIn, Email, SMS publishing
        pass

# Usage
system = CrisisResponseSystem()

# Generate and request approval
crisis = "Payment breach, 12K cards exposed..."
response = await system.generate_crisis_response(crisis)
print(f"Approval ID: {response['approval_id']}")

# Check status (would be called by webhook when approvers click links)
status = await system.check_approval_status(response['approval_id'])
print(f"Status: {status['status']}")

Level 3: Production with Real-Time Monitoring

Good for: 20+ crises/year | Setup time: 1 day

// Production with Real-Time Monitoring (20+ crises/year)
import Anthropic from '@anthropic-ai/sdk';
import { Queue, Worker } from 'bullmq';
import Redis from 'ioredis';
import { WebClient as SlackClient } from '@slack/web-api';
import axios from 'axios';

interface CrisisEvent {
  id: string;
  description: string;
  severity: 'low' | 'medium' | 'high' | 'critical';
  detected_at: Date;
  channels: string[];
  impact_metrics?: ImpactMetrics;
}

interface ImpactMetrics {
  social_reach: number;
  sentiment_score: number;
  media_mentions: number;
  support_tickets: number;
  customer_calls: number;
}

class ProductionCrisisSystem {
  private anthropic: Anthropic;
  private redis: Redis;
  private crisisQueue: Queue;
  private monitoringQueue: Queue;
  private slack: SlackClient;

  constructor() {
    this.anthropic = new Anthropic({
      apiKey: process.env.ANTHROPIC_API_KEY!,
    });
    this.redis = new Redis(process.env.REDIS_URL!);
    this.crisisQueue = new Queue('crisis-response', {
      connection: this.redis,
    });
    this.monitoringQueue = new Queue('crisis-monitoring', {
      connection: this.redis,
    });
    this.slack = new SlackClient(process.env.SLACK_BOT_TOKEN!);

    this.initializeWorkers();
  }

  private initializeWorkers() {
    // Worker 1: Process crisis events
    new Worker(
      'crisis-response',
      async (job) => {
        const crisis: CrisisEvent = job.data;
        await this.processCrisis(crisis);
      },
      { connection: this.redis }
    );

    // Worker 2: Monitor impact metrics
    new Worker(
      'crisis-monitoring',
      async (job) => {
        const crisisId: string = job.data.crisisId;
        await this.monitorImpact(crisisId);
      },
      { connection: this.redis }
    );
  }

  async detectAndRespond(crisisDescription: string): Promise<string> {
    // Step 1: Analyze and classify crisis
    const analysis = await this.analyzeCrisis(crisisDescription);

    // Step 2: Create crisis event
    const crisisEvent: CrisisEvent = {
      id: `crisis_${Date.now()}`,
      description: crisisDescription,
      severity: analysis.severity,
      detected_at: new Date(),
      channels: this.determineChannels(analysis.severity),
    };

    // Step 3: Store in Redis
    await this.redis.set(
      `crisis:${crisisEvent.id}`,
      JSON.stringify(crisisEvent),
      'EX',
      86400 * 7 // 7 days
    );

    // Step 4: Queue for processing
    await this.crisisQueue.add('process', crisisEvent, {
      priority: this.getPriority(crisisEvent.severity),
    });

    // Step 5: Start monitoring
    await this.monitoringQueue.add(
      'monitor',
      { crisisId: crisisEvent.id },
      {
        repeat: { every: 300000 }, // Every 5 minutes
      }
    );

    // Step 6: Alert crisis team
    await this.alertTeam(crisisEvent);

    return crisisEvent.id;
  }

  private async analyzeCrisis(description: string) {
    const prompt = `Analyze this crisis situation:

${description}

Provide:
1. Severity (low/medium/high/critical)
2. Crisis type (data_breach, service_outage, pr_issue, etc.)
3. Estimated impact (number of affected users)
4. Recommended response time (immediate/1hour/4hours/24hours)
5. Legal review required (yes/no)

Output as JSON.`;

    const response = await this.anthropic.messages.create({
      model: 'claude-3-5-sonnet-20241022',
      max_tokens: 1024,
      messages: [{ role: 'user', content: prompt }],
    });

    const content = response.content[0];
    if (content.type !== 'text') throw new Error('Invalid response');

    return JSON.parse(content.text);
  }

  private async processCrisis(crisis: CrisisEvent) {
    // Generate messages
    const messages = await this.generateMessages(crisis.description);

    // Route for approval if needed
    if (crisis.severity === 'high' || crisis.severity === 'critical') {
      const approvalId = await this.requestApproval(crisis, messages);
      await this.redis.set(
        `approval:${crisis.id}`,
        approvalId,
        'EX',
        3600
      );
      return;
    }

    // Auto-publish for low/medium severity
    await this.publishMessages(crisis.id, messages);
  }

  private async generateMessages(description: string) {
    const prompt = `Generate crisis messages for all channels.

Crisis: ${description}

Generate:
1. Twitter (under 280 chars, include status page link)
2. LinkedIn (professional, 200-300 words)
3. Email subject + body (detailed, empathetic)
4. SMS (under 160 chars)
5. Slack internal update (for team)
6. Status page update (technical details)

Output as JSON.`;

    const response = await this.anthropic.messages.create({
      model: 'claude-3-5-sonnet-20241022',
      max_tokens: 3000,
      messages: [{ role: 'user', content: prompt }],
    });

    const content = response.content[0];
    if (content.type !== 'text') throw new Error('Invalid response');

    return JSON.parse(content.text);
  }

  private async publishMessages(crisisId: string, messages: any) {
    const results = await Promise.allSettled([
      this.publishToTwitter(messages.twitter),
      this.publishToLinkedIn(messages.linkedin),
      this.sendEmails(messages.email_subject, messages.email_body),
      this.sendSMS(messages.sms),
      this.updateStatusPage(messages.status_page),
      this.notifySlack(messages.slack),
    ]);

    // Log results
    const publishLog = {
      crisis_id: crisisId,
      published_at: new Date(),
      channels: results.map((r, i) => ({
        channel: ['twitter', 'linkedin', 'email', 'sms', 'statuspage', 'slack'][i],
        status: r.status,
        error: r.status === 'rejected' ? (r as PromiseRejectedResult).reason : null,
      })),
    };

    await this.redis.lpush(
      `crisis:${crisisId}:publish_log`,
      JSON.stringify(publishLog)
    );

    return publishLog;
  }

  private async monitorImpact(crisisId: string) {
    // Fetch current metrics from various sources
    const metrics = await Promise.all([
      this.getSocialMediaReach(crisisId),
      this.analyzeSentiment(crisisId),
      this.getMediaMentions(crisisId),
      this.getSupportTicketCount(crisisId),
    ]);

    const impactMetrics: ImpactMetrics = {
      social_reach: metrics[0],
      sentiment_score: metrics[1],
      media_mentions: metrics[2],
      support_tickets: metrics[3],
      customer_calls: await this.getCallVolume(),
    };

    // Update crisis record
    const crisisData = await this.redis.get(`crisis:${crisisId}`);
    if (crisisData) {
      const crisis = JSON.parse(crisisData);
      crisis.impact_metrics = impactMetrics;
      await this.redis.set(
        `crisis:${crisisId}`,
        JSON.stringify(crisis),
        'KEEPTTL'
      );
    }

    // Alert if metrics exceed thresholds
    if (impactMetrics.sentiment_score < -0.5) {
      await this.alertTeam({
        id: crisisId,
        description: 'Negative sentiment detected',
        severity: 'high',
        detected_at: new Date(),
        channels: [],
        impact_metrics: impactMetrics,
      });
    }

    return impactMetrics;
  }

  private async getSocialMediaReach(crisisId: string): Promise<number> {
    // Aggregate reach across Twitter, LinkedIn, etc.
    // Implementation depends on your social media APIs
    return 125000; // Example
  }

  private async analyzeSentiment(crisisId: string): Promise<number> {
    // Use sentiment analysis API to analyze mentions
    // Return score from -1 (very negative) to 1 (very positive)
    return -0.3; // Example: slightly negative
  }

  private async getMediaMentions(crisisId: string): Promise<number> {
    // Query media monitoring service
    return 23; // Example
  }

  private async getSupportTicketCount(crisisId: string): Promise<number> {
    // Query support ticket system
    return 456; // Example
  }

  private async getCallVolume(): Promise<number> {
    // Query phone system for call volume
    return 89; // Example
  }

  private async alertTeam(crisis: CrisisEvent) {
    const message = `🚨 CRISIS ALERT 🚨\n\nSeverity: ${crisis.severity.toUpperCase()}\nID: ${crisis.id}\nDescription: ${crisis.description}\n\nDashboard: https://crisis-dashboard.company.com/${crisis.id}`;

    await this.slack.chat.postMessage({
      channel: process.env.SLACK_CRISIS_CHANNEL!,
      text: message,
      attachments: crisis.impact_metrics
        ? [
            {
              color: crisis.severity === 'critical' ? 'danger' : 'warning',
              fields: [
                {
                  title: 'Social Reach',
                  value: crisis.impact_metrics.social_reach.toLocaleString(),
                  short: true,
                },
                {
                  title: 'Sentiment',
                  value: crisis.impact_metrics.sentiment_score.toFixed(2),
                  short: true,
                },
                {
                  title: 'Media Mentions',
                  value: crisis.impact_metrics.media_mentions.toString(),
                  short: true,
                },
                {
                  title: 'Support Tickets',
                  value: crisis.impact_metrics.support_tickets.toString(),
                  short: true,
                },
              ],
            },
          ]
        : [],
    });
  }

  private determineChannels(severity: string): string[] {
    const baseChannels = ['twitter', 'statuspage'];
    if (severity === 'high' || severity === 'critical') {
      return [...baseChannels, 'linkedin', 'email', 'sms'];
    }
    return baseChannels;
  }

  private getPriority(severity: string): number {
    const priorities = { critical: 1, high: 2, medium: 3, low: 4 };
    return priorities[severity as keyof typeof priorities] || 5;
  }

  // Placeholder methods for actual publishing
  private async publishToTwitter(message: string) {
    // Implementation
  }
  private async publishToLinkedIn(message: string) {
    // Implementation
  }
  private async sendEmails(subject: string, body: string) {
    // Implementation
  }
  private async sendSMS(message: string) {
    // Implementation
  }
  private async updateStatusPage(message: string) {
    // Implementation
  }
  private async notifySlack(message: string) {
    // Implementation
  }
  private async requestApproval(crisis: CrisisEvent, messages: any) {
    // Implementation
    return 'approval_123';
  }
}

// Usage
const system = new ProductionCrisisSystem();
const crisisId = await system.detectAndRespond(
  'Payment breach, 12K cards exposed...'
);
console.log(`Crisis ${crisisId} being processed`);

When to Level Up

1

Start: Simple Multi-Channel

1-5 crises/year

  • Basic multi-channel publishing (Twitter, LinkedIn, Email, SMS)
  • Manual approval via email or Slack
  • Sequential publishing (one channel at a time)
  • Basic logging with console.log
2

Scale: Add Approval Workflows

5-20 crises/year

  • Automated approval routing based on severity
  • Email-based approval with one-click links
  • Validation checks (legal review, factual accuracy)
  • Auto-publish after all required approvals
  • Approval timeout handling (escalate after 30 mins)
3

Production: Real-Time Monitoring

20-50 crises/year

  • Queue-based processing with BullMQ (handles spikes)
  • Real-time impact monitoring (sentiment, reach, tickets)
  • Automated follow-up scheduling (4hr, 24hr, 7day updates)
  • Redis for state management (no data loss on crashes)
  • Slack alerts with live metrics dashboard
4

Enterprise: Multi-Region System

50+ crises/year

  • Multi-region deployment (publish from closest datacenter)
  • AI-powered crisis detection (monitors social media, news, support tickets)
  • Predictive impact modeling (forecast reach and sentiment trends)
  • Multi-language support (auto-translate for global teams)
  • Compliance automation (GDPR, SEC, industry-specific regulations)
  • Integration with crisis simulation training systems

Leadership-Specific Gotchas

The code examples above work. But crisis communication has unique challenges you need to handle.

Approval Timeout Handling - Don't Wait Forever

If CEO doesn't approve within 30 minutes during a critical crisis, you need escalation rules. Auto-escalate to COO or Board member. Don't let a crisis spiral because someone's in a meeting.

async function handleApprovalTimeout(approvalId: string) {
  const approval = await getApproval(approvalId);
  const elapsed = Date.now() - approval.created_at;
  
  // If 30 minutes passed and CEO hasn't approved
  if (elapsed > 30 * 60 * 1000 && !approval.ceo_approved) {
    // Escalate to COO
    await sendUrgentApprovalRequest({
      to: 'coo@company.com',
      subject: 'URGENT: Crisis Approval Escalation',
      body: `CEO hasn't responded to crisis approval in 30 mins.\n\nCrisis: ${approval.description}\n\nApprove now: ${approval.approve_link}`,
      priority: 'urgent'
    });
    
    // Also call COO's cell phone
    await makePhoneCall({
      to: process.env.COO_CELL_PHONE!,
      message: 'Urgent crisis approval needed. Check email immediately.'
    });
    
    // Log escalation
    await logEscalation(approvalId, 'CEO_timeout', 'escalated_to_COO');
  }
  
  // If 1 hour passed and still no approval, auto-publish with disclaimer
  if (elapsed > 60 * 60 * 1000 && !approval.any_approved) {
    await publishWithDisclaimer(approval.messages, {
      note: 'Published without full executive approval due to time-critical nature'
    });
    
    // Alert board
    await alertBoard(approvalId, 'auto_published_after_timeout');
  }
}

Rate Limiting Across Multiple Social Platforms

Twitter allows 300 tweets/3hrs. LinkedIn has different limits. If you're publishing updates every hour for 24 hours, you'll hit limits. Implement smart queuing and batching.

from ratelimit import limits, sleep_and_retry
import time

class SocialMediaPublisher:
    def __init__(self):
        self.twitter_calls = 0
        self.linkedin_calls = 0
        self.last_twitter_reset = time.time()
        self.last_linkedin_reset = time.time()
    
    @sleep_and_retry
    @limits(calls=300, period=10800)  # 300 calls per 3 hours
    def publish_to_twitter(self, message: str):
        """Rate-limited Twitter publishing"""
        # Actual Twitter API call
        twitter_client.tweet(message)
        self.twitter_calls += 1
        
        # Log rate limit usage
        print(f"Twitter: {self.twitter_calls}/300 calls used")
    
    @sleep_and_retry
    @limits(calls=100, period=86400)  # 100 calls per day
    def publish_to_linkedin(self, message: str):
        """Rate-limited LinkedIn publishing"""
        linkedin_client.create_share(message)
        self.linkedin_calls += 1
        
        print(f"LinkedIn: {self.linkedin_calls}/100 calls used")
    
    async def publish_with_retry(self, message: str, platform: str):
        """Retry with exponential backoff if rate limited"""
        max_retries = 3
        for attempt in range(max_retries):
            try:
                if platform == 'twitter':
                    self.publish_to_twitter(message)
                elif platform == 'linkedin':
                    self.publish_to_linkedin(message)
                return True
            except RateLimitException:
                if attempt < max_retries - 1:
                    wait_time = 2 ** attempt * 60  # 1min, 2min, 4min
                    print(f"Rate limited. Waiting {wait_time}s...")
                    await asyncio.sleep(wait_time)
                else:
                    # Queue for later
                    await self.queue_for_later(message, platform)
                    return False

Sentiment Analysis During Active Crisis

Social media sentiment changes fast during a crisis. You need real-time monitoring to know if your response is working or making things worse. Use streaming APIs and sentiment analysis.

import { TwitterApi } from 'twitter-api-v2';
import Anthropic from '@anthropic-ai/sdk';

class CrisisSentimentMonitor {
  private twitter: TwitterApi;
  private anthropic: Anthropic;
  private sentimentHistory: Array<{ timestamp: Date; score: number }> = [];

  async monitorHashtag(hashtag: string, crisisId: string) {
    // Start streaming mentions
    const stream = await this.twitter.v2.searchStream({
      'tweet.fields': ['created_at', 'text', 'public_metrics'],
      expansions: ['author_id'],
    });

    stream.on('data', async (tweet) => {
      if (tweet.data.text.includes(hashtag)) {
        // Analyze sentiment
        const sentiment = await this.analyzeSentiment(tweet.data.text);
        
        this.sentimentHistory.push({
          timestamp: new Date(),
          score: sentiment.score,
        });

        // Check if sentiment is deteriorating
        if (this.isGettingWorse()) {
          await this.alertLeadership({
            crisis_id: crisisId,
            message: 'Sentiment declining - consider revised messaging',
            current_score: sentiment.score,
            trend: 'negative',
            sample_tweets: await this.getMostNegativeTweets(5),
          });
        }
      }
    });
  }

  private async analyzeSentiment(text: string) {
    const response = await this.anthropic.messages.create({
      model: 'claude-3-5-sonnet-20241022',
      max_tokens: 256,
      messages: [
        {
          role: 'user',
          content: `Analyze sentiment of this tweet about our crisis response. Score from -1 (very negative) to 1 (very positive). Output JSON: {score: number, reasoning: string}\n\nTweet: ${text}`,
        },
      ],
    });

    const content = response.content[0];
    if (content.type !== 'text') throw new Error('Invalid response');
    return JSON.parse(content.text);
  }

  private isGettingWorse(): boolean {
    if (this.sentimentHistory.length < 10) return false;

    // Compare last 5 tweets to previous 5
    const recent = this.sentimentHistory.slice(-5);
    const previous = this.sentimentHistory.slice(-10, -5);

    const recentAvg = recent.reduce((sum, s) => sum + s.score, 0) / 5;
    const previousAvg = previous.reduce((sum, s) => sum + s.score, 0) / 5;

    // Alert if sentiment dropped by more than 0.2
    return recentAvg < previousAvg - 0.2;
  }

  private async getMostNegativeTweets(count: number) {
    // Return most negative tweets for context
    return this.sentimentHistory
      .sort((a, b) => a.score - b.score)
      .slice(0, count);
  }

  private async alertLeadership(alert: any) {
    // Send to Slack crisis channel
    await fetch(process.env.SLACK_WEBHOOK_URL!, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        text: `āš ļø Sentiment Alert: ${alert.message}`,
        attachments: [
          {
            color: 'danger',
            fields: [
              { title: 'Current Score', value: alert.current_score.toFixed(2), short: true },
              { title: 'Trend', value: alert.trend, short: true },
            ],
          },
        ],
      }),
    });
  }
}

Legal Compliance for Public Statements

Public companies have SEC disclosure requirements. Healthcare has HIPAA. Finance has specific regulations. Your crisis messages must comply. Build compliance checks into your approval workflow.

from typing import List, Dict
import re

class ComplianceChecker:
    def __init__(self, industry: str):
        self.industry = industry
        self.violations = []
    
    async def check_compliance(self, message: str, severity: str) -> Dict:
        """Run all compliance checks based on industry"""
        self.violations = []
        
        if self.industry == 'public_company':
            await self.check_sec_compliance(message, severity)
        elif self.industry == 'healthcare':
            await self.check_hipaa_compliance(message)
        elif self.industry == 'finance':
            await self.check_finra_compliance(message)
        
        # Universal checks
        await self.check_for_promises(message)
        await self.check_for_speculation(message)
        
        return {
            'compliant': len(self.violations) == 0,
            'violations': self.violations,
            'requires_legal_review': len(self.violations) > 0 or severity == 'critical'
        }
    
    async def check_sec_compliance(self, message: str, severity: str):
        """SEC disclosure requirements for public companies"""
        # Material events must be disclosed via 8-K filing
        material_keywords = ['breach', 'lawsuit', 'investigation', 'fraud', 'bankruptcy']
        
        if any(keyword in message.lower() for keyword in material_keywords):
            self.violations.append({
                'type': 'SEC_8K_REQUIRED',
                'message': 'Material event requires 8-K filing before public disclosure',
                'severity': 'critical',
                'action': 'File 8-K with SEC before publishing'
            })
        
        # Forward-looking statements need safe harbor language
        future_keywords = ['will', 'expect', 'anticipate', 'plan', 'forecast']
        if any(keyword in message.lower() for keyword in future_keywords):
            if 'forward-looking' not in message.lower():
                self.violations.append({
                    'type': 'SAFE_HARBOR_MISSING',
                    'message': 'Forward-looking statements need safe harbor disclaimer',
                    'severity': 'medium',
                    'action': 'Add: "These are forward-looking statements subject to risks..."'
                })
    
    async def check_hipaa_compliance(self, message: str):
        """HIPAA compliance for healthcare"""
        # Check for PHI (Protected Health Information)
        phi_patterns = [
            r'\b\d{3}-\d{2}-\d{4}\b',  # SSN
            r'\b[A-Z][a-z]+ [A-Z][a-z]+\b',  # Names
            r'\b\d{1,5}\s\w+\s(Street|St|Avenue|Ave|Road|Rd)\b',  # Addresses
        ]
        
        for pattern in phi_patterns:
            if re.search(pattern, message):
                self.violations.append({
                    'type': 'PHI_DETECTED',
                    'message': 'Potential PHI found in message',
                    'severity': 'critical',
                    'action': 'Remove or redact PHI before publishing'
                })
    
    async def check_for_promises(self, message: str):
        """Don't make promises you can't keep"""
        promise_phrases = [
            'guarantee', 'promise', 'will never happen again',
            'completely resolved', 'fully secure'
        ]
        
        for phrase in promise_phrases:
            if phrase in message.lower():
                self.violations.append({
                    'type': 'OVERPROMISE',
                    'message': f'Phrase "{phrase}" makes absolute promise',
                    'severity': 'medium',
                    'action': 'Use "working to" or "committed to" instead'
                })
    
    async def check_for_speculation(self, message: str):
        """Avoid speculation about causes or responsibility"""
        speculation_phrases = [
            'we believe the cause was',
            'likely caused by',
            'probably due to'
        ]
        
        for phrase in speculation_phrases:
            if phrase in message.lower():
                self.violations.append({
                    'type': 'SPECULATION',
                    'message': 'Message contains speculation about cause',
                    'severity': 'high',
                    'action': 'Wait for investigation results before stating cause'
                })

# Usage
checker = ComplianceChecker(industry='public_company')
result = await checker.check_compliance(message, severity='high')

if not result['compliant']:
    # Block publication and route to legal
    await route_to_legal(result['violations'])
else:
    # Proceed with approval workflow
    await request_approvals(message)

Multi-Language Support for Global Teams

If you operate globally, your crisis affects teams in different countries. Auto-translate your crisis messages to local languages, but have native speakers review before publishing.

import Anthropic from '@anthropic-ai/sdk';

interface TranslationRequest {
  text: string;
  target_languages: string[];
  requires_review: boolean;
}

class GlobalCrisisPublisher {
  private anthropic: Anthropic;
  private native_reviewers: Map<string, string[]>;

  constructor() {
    this.anthropic = new Anthropic({
      apiKey: process.env.ANTHROPIC_API_KEY!,
    });
    
    // Map of language -> reviewer emails
    this.native_reviewers = new Map([
      ['es', ['maria@company.com', 'carlos@company.com']],
      ['fr', ['pierre@company.com']],
      ['de', ['hans@company.com']],
      ['ja', ['yuki@company.com']],
      ['zh', ['wei@company.com']],
    ]);
  }

  async translateAndPublish(request: TranslationRequest) {
    const translations = new Map<string, string>();

    // Translate to all target languages
    for (const lang of request.target_languages) {
      const translated = await this.translateMessage(request.text, lang);
      translations.set(lang, translated);
    }

    if (request.requires_review) {
      // Send to native speakers for review
      const reviewIds = await this.requestNativeReview(translations);
      
      // Wait for reviews (with timeout)
      const approved = await this.waitForReviews(reviewIds, 3600000); // 1 hour
      
      if (!approved) {
        // Publish English only if reviews timeout
        await this.publishEnglishOnly(request.text);
        return { status: 'partial', published_languages: ['en'] };
      }
    }

    // Publish all translations
    await this.publishAllLanguages(translations);
    return { status: 'complete', published_languages: Array.from(translations.keys()) };
  }

  private async translateMessage(text: string, targetLang: string): Promise<string> {
    const langNames: Record<string, string> = {
      es: 'Spanish',
      fr: 'French',
      de: 'German',
      ja: 'Japanese',
      zh: 'Chinese',
    };

    const prompt = `Translate this crisis communication message to ${langNames[targetLang]}. Maintain the tone (urgent but professional) and ensure cultural appropriateness.\n\nOriginal message:\n${text}\n\nTranslated message:`;

    const response = await this.anthropic.messages.create({
      model: 'claude-3-5-sonnet-20241022',
      max_tokens: 1024,
      messages: [{ role: 'user', content: prompt }],
    });

    const content = response.content[0];
    if (content.type !== 'text') throw new Error('Invalid response');
    return content.text;
  }

  private async requestNativeReview(
    translations: Map<string, string>
  ): Promise<string[]> {
    const reviewIds: string[] = [];

    for (const [lang, text] of translations.entries()) {
      const reviewers = this.native_reviewers.get(lang) || [];
      
      for (const reviewer of reviewers) {
        const reviewId = `review_${Date.now()}_${lang}_${reviewer}`;
        
        await this.sendReviewRequest({
          reviewId,
          reviewer,
          language: lang,
          text,
          deadline: new Date(Date.now() + 3600000), // 1 hour
        });
        
        reviewIds.push(reviewId);
      }
    }

    return reviewIds;
  }

  private async sendReviewRequest(request: any) {
    // Send email to native speaker
    const emailBody = `URGENT: Crisis Message Translation Review

Language: ${request.language}
Deadline: ${request.deadline.toISOString()}

Translated message:
${request.text}

Approve: https://crisis.company.com/review/${request.reviewId}/approve
Request changes: https://crisis.company.com/review/${request.reviewId}/edit

This is time-sensitive. Please review within 1 hour.`;

    // Send via email API
    await sendEmail({
      to: request.reviewer,
      subject: `URGENT: ${request.language.toUpperCase()} Translation Review`,
      body: emailBody,
      priority: 'urgent',
    });
  }

  private async waitForReviews(
    reviewIds: string[],
    timeout: number
  ): Promise<boolean> {
    const startTime = Date.now();
    
    while (Date.now() - startTime < timeout) {
      const allApproved = await this.checkAllReviewsApproved(reviewIds);
      if (allApproved) return true;
      
      await new Promise((resolve) => setTimeout(resolve, 10000)); // Check every 10s
    }
    
    return false; // Timeout
  }

  private async checkAllReviewsApproved(reviewIds: string[]): Promise<boolean> {
    // Check database for review status
    // Implementation depends on your DB
    return false;
  }

  private async publishEnglishOnly(text: string) {
    // Fallback: publish English version only
    await this.publishToAllChannels({ en: text });
  }

  private async publishAllLanguages(translations: Map<string, string>) {
    // Publish to regional social media accounts
    const promises = [];
    
    for (const [lang, text] of translations.entries()) {
      promises.push(this.publishToRegionalChannels(lang, text));
    }
    
    await Promise.all(promises);
  }

  private async publishToRegionalChannels(lang: string, text: string) {
    // Publish to language-specific Twitter/LinkedIn accounts
    // e.g., @CompanyES for Spanish, @CompanyFR for French
  }

  private async publishToAllChannels(translations: Record<string, string>) {
    // Implementation
  }
}

// Usage
const publisher = new GlobalCrisisPublisher();
await publisher.translateAndPublish({
  text: 'We experienced a security incident...',
  target_languages: ['es', 'fr', 'de', 'ja'],
  requires_review: true,
});

Cost Calculator

Manual Crisis Response

Crisis manager time (2-4 hours per crisis)
At $100/hr for senior manager
$200-400
PR team coordination (1-2 hours)
Multiple stakeholders, meetings
$150-300
Legal review time (1-2 hours)
At $400/hr for legal counsel
$400-800
Inconsistent messaging errors
Reputational damage, customer churn
$5,000-50,000
Delayed response (lost customers)
Customers switch to competitors
$10,000-100,000
Total:$15,750-151,500 per crisis
Per crisis event

Limitations:

  • • 2-4 hour response time (too slow)
  • • Inconsistent messaging across channels
  • • No real-time impact tracking
  • • Approval bottlenecks
  • • Can't scale beyond 5-10 crises/year

Automated Crisis System

LLM API costs (Claude/GPT-4)
Message generation + sentiment analysis
$50-100
Social media API costs
Twitter, LinkedIn publishing
$100-200
Email/SMS costs (SendGrid + Twilio)
Based on customer count
$200-500
Infrastructure (Redis, queues, monitoring)
Monthly hosting costs
$100-300
Manager approval time (15-30 mins)
Quick review, not full drafting
$25-50
Total:$475-1,150 per crisis
Per crisis event

Benefits:

  • āœ“ 30-second to 5-minute response time
  • āœ“ Consistent messaging across all channels
  • āœ“ Real-time sentiment tracking
  • āœ“ Automated approval workflows
  • āœ“ Scales to 50+ crises/year easily
  • āœ“ Detailed impact analytics
See Note Below saved
97% cost reduction | $68,879/month | $414,062/year
Note: Event-based cost (average of 5 crises/year range)
šŸ’” First crisis pays for entire system setup
šŸ‘”

Want This Running in Your Organization?

We build custom crisis response systems that integrate with your existing tools - Slack, email, social media, status pages. Production-ready in 2-4 weeks.