Claude-skill-registry communication-systems
Email, notifications, and messaging system patterns
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/communication-systems" ~/.claude/skills/majiayu000-claude-skill-registry-communication-systems && rm -rf "$T"
manifest:
skills/data/communication-systems/SKILL.mdsource content
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