Claude Code Plugins

Community-maintained marketplace

Feedback
0
0

Email, notifications, and messaging system patterns

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name communication-systems
description Email, notifications, and messaging system patterns
domain domain-applications
version 1.0.0
tags email, notifications, push, webhooks, messaging
triggers [object Object]

Communication Systems

Overview

Building email systems, push notifications, in-app messaging, and webhook integrations.


Email Systems

Transactional Email

import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

interface EmailOptions {
  to: string | string[];
  subject: string;
  html?: string;
  text?: string;
  template?: string;
  data?: Record<string, any>;
  attachments?: Array<{
    filename: string;
    content: Buffer | string;
  }>;
}

async function sendEmail(options: EmailOptions) {
  let html = options.html;

  // Use template if specified
  if (options.template) {
    html = await renderTemplate(options.template, options.data);
  }

  const { data, error } = await resend.emails.send({
    from: 'noreply@example.com',
    to: options.to,
    subject: options.subject,
    html,
    text: options.text,
    attachments: options.attachments,
  });

  if (error) {
    console.error('Email send failed:', error);
    throw error;
  }

  // Log for tracking
  await prisma.emailLog.create({
    data: {
      messageId: data.id,
      to: Array.isArray(options.to) ? options.to.join(',') : options.to,
      subject: options.subject,
      template: options.template,
      status: 'sent',
    },
  });

  return data;
}

// Email templates with React Email
import { render } from '@react-email/render';
import { WelcomeEmail } from './templates/WelcomeEmail';
import { PasswordResetEmail } from './templates/PasswordResetEmail';

const templates = {
  welcome: WelcomeEmail,
  passwordReset: PasswordResetEmail,
};

async function renderTemplate(name: string, data: Record<string, any>) {
  const Template = templates[name];
  if (!Template) throw new Error(`Template ${name} not found`);

  return render(<Template {...data} />);
}

// React Email template
import {
  Html, Head, Body, Container, Text, Button, Img,
} from '@react-email/components';

function WelcomeEmail({ name, actionUrl }: { name: string; actionUrl: string }) {
  return (
    <Html>
      <Head />
      <Body style={{ fontFamily: 'Arial, sans-serif' }}>
        <Container>
          <Img src="https://example.com/logo.png" width="120" height="40" alt="Logo" />
          <Text>Hi {name},</Text>
          <Text>Welcome to our platform! Get started by setting up your account.</Text>
          <Button
            href={actionUrl}
            style={{ background: '#007bff', color: '#fff', padding: '12px 24px' }}
          >
            Get Started
          </Button>
        </Container>
      </Body>
    </Html>
  );
}

Email Queue

import Bull from 'bull';

const emailQueue = new Bull('email', process.env.REDIS_URL);

// Add to queue
async function queueEmail(options: EmailOptions, delay?: number) {
  return emailQueue.add('send', options, {
    delay,
    attempts: 3,
    backoff: { type: 'exponential', delay: 60000 },
  });
}

// Process queue
emailQueue.process('send', async (job) => {
  await sendEmail(job.data);
});

// Handle failures
emailQueue.on('failed', async (job, error) => {
  console.error(`Email job ${job.id} failed:`, error);

  await prisma.emailLog.update({
    where: { jobId: job.id },
    data: { status: 'failed', error: error.message },
  });
});

// Bulk email with rate limiting
async function sendBulkEmail(recipients: string[], template: string, data: Record<string, any>) {
  const jobs = recipients.map((to, index) => ({
    name: 'send',
    data: { to, template, data },
    opts: { delay: index * 100 }, // Stagger sends
  }));

  await emailQueue.addBulk(jobs);
}

Push Notifications

Web Push

import webpush from 'web-push';

webpush.setVapidDetails(
  'mailto:admin@example.com',
  process.env.VAPID_PUBLIC_KEY!,
  process.env.VAPID_PRIVATE_KEY!
);

interface PushSubscription {
  endpoint: string;
  keys: {
    p256dh: string;
    auth: string;
  };
}

// Store subscription
async function saveSubscription(userId: string, subscription: PushSubscription) {
  await prisma.pushSubscription.upsert({
    where: { endpoint: subscription.endpoint },
    update: { keys: subscription.keys },
    create: {
      userId,
      endpoint: subscription.endpoint,
      keys: subscription.keys,
    },
  });
}

// Send push notification
async function sendPush(userId: string, payload: {
  title: string;
  body: string;
  icon?: string;
  url?: string;
  data?: Record<string, any>;
}) {
  const subscriptions = await prisma.pushSubscription.findMany({
    where: { userId },
  });

  const results = await Promise.allSettled(
    subscriptions.map(async (sub) => {
      try {
        await webpush.sendNotification(
          { endpoint: sub.endpoint, keys: sub.keys },
          JSON.stringify(payload)
        );
      } catch (error) {
        if (error.statusCode === 410) {
          // Subscription expired, remove it
          await prisma.pushSubscription.delete({ where: { id: sub.id } });
        }
        throw error;
      }
    })
  );

  return results;
}

// Service worker handler
// public/sw.js
self.addEventListener('push', (event) => {
  const data = event.data.json();

  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: data.icon || '/icon-192.png',
      data: data,
    })
  );
});

self.addEventListener('notificationclick', (event) => {
  event.notification.close();

  if (event.notification.data.url) {
    event.waitUntil(clients.openWindow(event.notification.data.url));
  }
});

Mobile Push (FCM)

import admin from 'firebase-admin';

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
});

interface MobileNotification {
  title: string;
  body: string;
  imageUrl?: string;
  data?: Record<string, string>;
}

async function sendMobilePush(
  tokens: string[],
  notification: MobileNotification
) {
  const message: admin.messaging.MulticastMessage = {
    tokens,
    notification: {
      title: notification.title,
      body: notification.body,
      imageUrl: notification.imageUrl,
    },
    data: notification.data,
    android: {
      priority: 'high',
      notification: {
        sound: 'default',
        clickAction: 'OPEN_ACTIVITY',
      },
    },
    apns: {
      payload: {
        aps: {
          sound: 'default',
          badge: 1,
        },
      },
    },
  };

  const response = await admin.messaging().sendEachForMulticast(message);

  // Handle failures
  response.responses.forEach((resp, idx) => {
    if (!resp.success) {
      const errorCode = resp.error?.code;
      if (
        errorCode === 'messaging/invalid-registration-token' ||
        errorCode === 'messaging/registration-token-not-registered'
      ) {
        // Remove invalid token
        removeDeviceToken(tokens[idx]);
      }
    }
  });

  return response;
}

In-App Notifications

interface Notification {
  id: string;
  userId: string;
  type: string;
  title: string;
  message: string;
  data?: Record<string, any>;
  read: boolean;
  createdAt: Date;
}

// Create notification
async function createNotification(params: {
  userId: string;
  type: string;
  title: string;
  message: string;
  data?: Record<string, any>;
}) {
  const notification = await prisma.notification.create({
    data: {
      ...params,
      read: false,
    },
  });

  // Send real-time update
  await pubsub.publish(`notifications:${params.userId}`, {
    type: 'NEW_NOTIFICATION',
    notification,
  });

  return notification;
}

// Get notifications with pagination
async function getNotifications(userId: string, options: {
  page?: number;
  limit?: number;
  unreadOnly?: boolean;
}) {
  const { page = 1, limit = 20, unreadOnly = false } = options;

  const where = {
    userId,
    ...(unreadOnly && { read: false }),
  };

  const [notifications, total, unreadCount] = await Promise.all([
    prisma.notification.findMany({
      where,
      orderBy: { createdAt: 'desc' },
      skip: (page - 1) * limit,
      take: limit,
    }),
    prisma.notification.count({ where }),
    prisma.notification.count({ where: { userId, read: false } }),
  ]);

  return { notifications, total, unreadCount };
}

// Mark as read
async function markAsRead(userId: string, notificationIds: string[]) {
  await prisma.notification.updateMany({
    where: {
      id: { in: notificationIds },
      userId,
    },
    data: { read: true },
  });
}

// React hook for notifications
function useNotifications() {
  const [notifications, setNotifications] = useState<Notification[]>([]);
  const [unreadCount, setUnreadCount] = useState(0);

  useEffect(() => {
    // Initial fetch
    fetchNotifications().then(({ notifications, unreadCount }) => {
      setNotifications(notifications);
      setUnreadCount(unreadCount);
    });

    // Subscribe to real-time updates
    const unsubscribe = subscribeToNotifications((notification) => {
      setNotifications((prev) => [notification, ...prev]);
      setUnreadCount((prev) => prev + 1);
    });

    return unsubscribe;
  }, []);

  return { notifications, unreadCount, markAsRead };
}

Webhooks

interface Webhook {
  id: string;
  url: string;
  secret: string;
  events: string[];
  active: boolean;
}

// Register webhook
async function registerWebhook(params: {
  url: string;
  events: string[];
}) {
  const secret = crypto.randomBytes(32).toString('hex');

  return prisma.webhook.create({
    data: {
      url: params.url,
      events: params.events,
      secret,
      active: true,
    },
  });
}

// Send webhook
async function sendWebhook(webhookId: string, event: string, payload: any) {
  const webhook = await prisma.webhook.findUnique({ where: { id: webhookId } });
  if (!webhook || !webhook.active) return;

  const timestamp = Date.now().toString();
  const body = JSON.stringify({ event, data: payload, timestamp });

  // Create signature
  const signature = crypto
    .createHmac('sha256', webhook.secret)
    .update(body)
    .digest('hex');

  try {
    const response = await fetch(webhook.url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Webhook-Signature': signature,
        'X-Webhook-Timestamp': timestamp,
      },
      body,
    });

    await prisma.webhookLog.create({
      data: {
        webhookId,
        event,
        payload,
        responseStatus: response.status,
        success: response.ok,
      },
    });

    // Disable after multiple failures
    if (!response.ok) {
      await handleWebhookFailure(webhookId);
    }
  } catch (error) {
    await prisma.webhookLog.create({
      data: {
        webhookId,
        event,
        payload,
        error: error.message,
        success: false,
      },
    });

    await handleWebhookFailure(webhookId);
  }
}

// Verify webhook signature (receiver side)
function verifyWebhookSignature(
  body: string,
  signature: string,
  secret: string
): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Related Skills

  • [[realtime-systems]] - Real-time messaging
  • [[backend]] - API development
  • [[reliability-engineering]] - Delivery guarantees