import { qrGenerator } from './qr-generator'; import { canvasImageGenerator } from './canvas-image-generator'; interface SocialPost { text: string; imageUrl: string; hashtags: string[]; dimensions: { width: number; height: number }; platformSpecific: any; } interface EventData { id: string; title: string; description: string; venue: string; start_time: string; end_time: string; slug: string; image_url?: string; social_links?: any; website_url?: string; organizations: { name: string; logo?: string; social_links?: any; website_url?: string; }; } class SocialMediaGenerator { private platformDimensions = { facebook: { width: 1200, height: 630 }, instagram: { width: 1080, height: 1080 }, twitter: { width: 1200, height: 675 }, linkedin: { width: 1200, height: 627 } }; private platformLimits = { facebook: { textLimit: 2000 }, instagram: { textLimit: 2200 }, twitter: { textLimit: 280 }, linkedin: { textLimit: 3000 } }; /** * Generate social media post for specific platform */ async generatePost(event: EventData, platform: string): Promise { const dimensions = this.platformDimensions[platform] || this.platformDimensions.facebook; // Generate post text const text = this.generatePostText(event, platform); // Generate hashtags const hashtags = this.generateHashtags(event, platform); // Generate image const imageUrl = await this.generateSocialImage(event, platform, dimensions); // Platform-specific configuration const platformSpecific = this.getPlatformSpecificConfig(event, platform); return { text, imageUrl, hashtags, dimensions, platformSpecific }; } /** * Generate platform-appropriate post text */ private generatePostText(event: EventData, platform: string): string { const eventDate = new Date(event.start_time); const formattedDate = eventDate.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); const formattedTime = eventDate.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }); // Get social handles from event or organization const socialLinks = event.social_links || event.organizations.social_links || {}; const orgHandle = this.getSocialHandle(socialLinks, platform); // Get ticket URL const ticketUrl = `${import.meta.env.PUBLIC_SITE_URL || 'https://portal.blackcanyontickets.com'}/e/${event.slug}`; const templates = { facebook: `🎉 You're Invited: ${event.title} 📅 ${formattedDate} at ${formattedTime} 📍 ${event.venue} ${event.description ? event.description.substring(0, 300) + (event.description.length > 300 ? '...' : '') : 'Join us for an unforgettable experience!'} 🎫 Get your tickets now: ${ticketUrl} ${orgHandle ? `Follow us: ${orgHandle}` : ''} #Events #Tickets #${event.venue.replace(/\s+/g, '')}`, instagram: `✨ ${event.title} ✨ 📅 ${formattedDate} ⏰ ${formattedTime} 📍 ${event.venue} ${event.description ? event.description.substring(0, 200) + '...' : 'An experience you won\'t want to miss! 🎭'} Link in bio for tickets 🎫 👆 or scan the QR code in this post ${orgHandle ? `Follow ${orgHandle} for more events` : ''}`, twitter: `🎉 ${event.title} 📅 ${formattedDate} • ${formattedTime} 📍 ${event.venue} 🎫 Tickets: ${ticketUrl} ${orgHandle || ''}`, linkedin: `Professional Event Announcement: ${event.title} Date: ${formattedDate} Time: ${formattedTime} Venue: ${event.venue} ${event.description ? event.description.substring(0, 400) : 'We invite you to join us for this professional gathering.'} Secure your tickets: ${ticketUrl} ${orgHandle ? `Connect with us: ${orgHandle}` : ''} #ProfessionalEvents #Networking #${event.organizations.name.replace(/\s+/g, '')}` }; const text = templates[platform] || templates.facebook; const limit = this.platformLimits[platform]?.textLimit || 2000; return text.length > limit ? text.substring(0, limit - 3) + '...' : text; } /** * Generate relevant hashtags for the event */ private generateHashtags(event: EventData, platform: string): string[] { const baseHashtags = [ 'Events', 'Tickets', event.organizations.name.replace(/\s+/g, ''), event.venue.replace(/\s+/g, ''), 'EventTickets' ]; // Add date-based hashtags const eventDate = new Date(event.start_time); const month = eventDate.toLocaleDateString('en-US', { month: 'long' }); const year = eventDate.getFullYear(); baseHashtags.push(`${month}${year}`); // Platform-specific hashtag strategies const platformHashtags = { facebook: [...baseHashtags, 'LocalEvents', 'Community'], instagram: [...baseHashtags, 'InstaEvent', 'EventPlanning', 'Memories', 'Experience'], twitter: [...baseHashtags.slice(0, 3)], // Twitter users prefer fewer hashtags linkedin: [...baseHashtags, 'ProfessionalEvents', 'Networking', 'Business'] }; return platformHashtags[platform] || baseHashtags; } /** * Generate social media image with event details */ private async generateSocialImage( event: EventData, platform: string, dimensions: { width: number; height: number } ): Promise { // Generate QR code for the event const qrCode = await qrGenerator.generateEventQR(event.slug, { size: platform === 'instagram' ? 200 : 150, color: { dark: '#000000', light: '#FFFFFF' } }); // Generate branded image with canvas const imageConfig = { width: dimensions.width, height: dimensions.height, platform, event, qrCode: qrCode.dataUrl, backgroundColor: this.getPlatformTheme(platform).backgroundColor, textColor: this.getPlatformTheme(platform).textColor, accentColor: this.getPlatformTheme(platform).accentColor }; const imageUrl = await canvasImageGenerator.generateSocialImage(imageConfig); return imageUrl; } /** * Get platform-specific theme colors */ private getPlatformTheme(platform: string) { const themes = { facebook: { backgroundColor: ['#1877F2', '#4267B2'], textColor: '#FFFFFF', accentColor: '#FF6B35' }, instagram: { backgroundColor: ['#E4405F', '#F77737', '#FCAF45'], textColor: '#FFFFFF', accentColor: '#C13584' }, twitter: { backgroundColor: ['#1DA1F2', '#0084b4'], textColor: '#FFFFFF', accentColor: '#FF6B6B' }, linkedin: { backgroundColor: ['#0077B5', '#004182'], textColor: '#FFFFFF', accentColor: '#2867B2' } }; return themes[platform] || themes.facebook; } /** * Get social handle for platform */ private getSocialHandle(socialLinks: any, platform: string): string { if (!socialLinks || !socialLinks[platform]) { return ''; } const url = socialLinks[platform]; // Extract handle from URL if (platform === 'twitter') { const match = url.match(/twitter\.com\/([^\/]+)/); return match ? `@${match[1]}` : ''; } else if (platform === 'instagram') { const match = url.match(/instagram\.com\/([^\/]+)/); return match ? `@${match[1]}` : ''; } else if (platform === 'facebook') { const match = url.match(/facebook\.com\/([^\/]+)/); return match ? `facebook.com/${match[1]}` : ''; } else if (platform === 'linkedin') { return url; } return url; } /** * Get platform-specific configuration */ private getPlatformSpecificConfig(event: EventData, platform: string) { const ticketUrl = `${import.meta.env.PUBLIC_SITE_URL || 'https://portal.blackcanyontickets.com'}/e/${event.slug}`; return { facebook: { linkUrl: ticketUrl, callToAction: 'Get Tickets', eventType: 'ticket_sales' }, instagram: { linkInBio: true, storyLink: ticketUrl, callToAction: 'Link in Bio 👆' }, twitter: { linkUrl: ticketUrl, tweetIntent: `Check out ${event.title} - ${ticketUrl}`, callToAction: 'Get Tickets' }, linkedin: { linkUrl: ticketUrl, eventType: 'professional', callToAction: 'Secure Your Spot' } }[platform]; } /** * Generate multiple variations of a post */ async generateVariations(event: EventData, platform: string, count: number = 3): Promise { const variations: SocialPost[] = []; for (let i = 0; i < count; i++) { // Modify the approach slightly for each variation const variation = await this.generatePost(event, platform); // TODO: Implement different text styles, image layouts, etc. variations.push(variation); } return variations; } /** * Get optimal posting times for platform */ getOptimalPostingTimes(platform: string): string[] { const times = { facebook: ['9:00 AM', '1:00 PM', '7:00 PM'], instagram: ['11:00 AM', '2:00 PM', '8:00 PM'], twitter: ['8:00 AM', '12:00 PM', '6:00 PM'], linkedin: ['8:00 AM', '10:00 AM', '5:00 PM'] }; return times[platform] || times.facebook; } } export const socialMediaGenerator = new SocialMediaGenerator();