mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-27 21:01:03 +08:00
149 lines
5.3 KiB
TypeScript
149 lines
5.3 KiB
TypeScript
/**
|
|
* Sends a message to Discord webhook with rich metadata
|
|
*/
|
|
export async function sendToDiscordWebhook(data: {
|
|
message: string;
|
|
username?: string;
|
|
metadata?: {
|
|
url?: string;
|
|
pathname?: string;
|
|
timestamp?: string;
|
|
userAgent?: string;
|
|
viewport?: string;
|
|
isHomePage?: boolean;
|
|
isScrolled?: boolean;
|
|
messageLength?: number;
|
|
messageType?: string;
|
|
sessionMessageCount?: number;
|
|
timeOnPage?: number;
|
|
scrollDepth?: number;
|
|
referrer?: string;
|
|
language?: string;
|
|
timezone?: string;
|
|
chatExpanded?: boolean;
|
|
};
|
|
}) {
|
|
const webhookUrl = process.env.DISCORD_WEBHOOK_URL;
|
|
|
|
if (!webhookUrl) {
|
|
console.warn('Discord webhook URL not configured');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const { message, username, metadata } = data;
|
|
|
|
// Create a rich embed with metadata
|
|
const embed = {
|
|
title: "💬 AI Chat Message",
|
|
description: message.length > 100 ? message.substring(0, 100) + "..." : message,
|
|
color: metadata?.messageType === 'starter-prompt' ? 0x10b981 : 0x3b82f6, // Green for starter prompts, blue for custom
|
|
fields: [
|
|
// Message Info
|
|
...(metadata?.messageType ? [{ name: "📝 Type", value: metadata.messageType === 'starter-prompt' ? "Starter Prompt" : "Custom Message", inline: true }] : []),
|
|
...(metadata?.messageLength ? [{ name: "📏 Length", value: `${metadata.messageLength} chars`, inline: true }] : []),
|
|
...(metadata?.chatExpanded !== undefined ? [{ name: "🔍 Chat Mode", value: metadata.chatExpanded ? "Expanded" : "Normal", inline: true }] : []),
|
|
|
|
// Page Context
|
|
...(metadata?.pathname ? [{ name: "📄 Page", value: metadata.pathname, inline: true }] : []),
|
|
...(metadata?.isHomePage !== undefined ? [{ name: "🏠 Homepage", value: metadata.isHomePage ? "Yes" : "No", inline: true }] : []),
|
|
...(metadata?.isScrolled !== undefined ? [{ name: "📜 Scrolled", value: metadata.isScrolled ? "Yes" : "No", inline: true }] : []),
|
|
|
|
// Session Data
|
|
...(metadata?.sessionMessageCount ? [{ name: "💬 Session Messages", value: metadata.sessionMessageCount.toString(), inline: true }] : []),
|
|
...(metadata?.timeOnPage ? [{ name: "⏱️ Time on Page", value: formatTime(metadata.timeOnPage), inline: true }] : []),
|
|
...(metadata?.scrollDepth !== undefined ? [{ name: "📊 Scroll Depth", value: `${metadata.scrollDepth}%`, inline: true }] : []),
|
|
|
|
// Technical Info
|
|
...(metadata?.viewport ? [{ name: "📱 Viewport", value: metadata.viewport, inline: true }] : []),
|
|
...(metadata?.language ? [{ name: "🌐 Language", value: metadata.language, inline: true }] : []),
|
|
...(metadata?.timezone ? [{ name: "⏰ Timezone", value: metadata.timezone, inline: true }] : []),
|
|
],
|
|
timestamp: metadata?.timestamp || new Date().toISOString(),
|
|
footer: {
|
|
text: "Stack Auth Docs AI Chat",
|
|
icon_url: "https://cdn.discordapp.com/embed/avatars/0.png"
|
|
}
|
|
};
|
|
|
|
// Add full message as a separate field if it was truncated
|
|
if (message.length > 100) {
|
|
embed.fields.unshift({ name: "📝 Full Message", value: message, inline: false });
|
|
}
|
|
|
|
// Extract browser info from user agent
|
|
const browserInfo = extractBrowserInfo(metadata?.userAgent || '');
|
|
if (browserInfo) {
|
|
embed.fields.push({ name: "🌐 Browser", value: browserInfo, inline: true });
|
|
}
|
|
|
|
// Add referrer info
|
|
if (metadata?.referrer && metadata.referrer !== 'Direct') {
|
|
embed.fields.push({ name: "🔗 Referrer", value: metadata.referrer, inline: true });
|
|
}
|
|
|
|
const response = await fetch(webhookUrl, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
username: username || 'Stack Auth Docs User',
|
|
avatar_url: 'https://cdn.discordapp.com/embed/avatars/0.png',
|
|
embeds: [embed],
|
|
}),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.error('Failed to send message to Discord:', response.statusText);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error sending message to Discord:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Format time in seconds to a human-readable format
|
|
*/
|
|
function formatTime(seconds: number): string {
|
|
if (seconds < 60) return `${seconds}s`;
|
|
const minutes = Math.floor(seconds / 60);
|
|
const remainingSeconds = seconds % 60;
|
|
return `${minutes}m ${remainingSeconds}s`;
|
|
}
|
|
|
|
/**
|
|
* Extract browser and OS info from user agent
|
|
*/
|
|
function extractBrowserInfo(userAgent: string): string | null {
|
|
if (!userAgent) return null;
|
|
|
|
// Extract browser
|
|
let browser = 'Unknown';
|
|
if (userAgent.includes('Chrome/')) {
|
|
browser = 'Chrome';
|
|
} else if (userAgent.includes('Firefox/')) {
|
|
browser = 'Firefox';
|
|
} else if (userAgent.includes('Safari/') && !userAgent.includes('Chrome/')) {
|
|
browser = 'Safari';
|
|
} else if (userAgent.includes('Edge/')) {
|
|
browser = 'Edge';
|
|
}
|
|
|
|
// Extract OS
|
|
let os = 'Unknown';
|
|
if (userAgent.includes('Windows NT')) {
|
|
os = 'Windows';
|
|
} else if (userAgent.includes('Mac OS X')) {
|
|
os = 'macOS';
|
|
} else if (userAgent.includes('Linux')) {
|
|
os = 'Linux';
|
|
} else if (userAgent.includes('iPhone') || userAgent.includes('iPad')) {
|
|
os = 'iOS';
|
|
} else if (userAgent.includes('Android')) {
|
|
os = 'Android';
|
|
}
|
|
|
|
return `${browser} on ${os}`;
|
|
}
|