11import { getCollection , getEntry } from "astro:content" ;
22import type { APIRoute } from "astro" ;
33
4+ // Get @username from Twitter URL
5+ function getTwitterUsername ( url : string ) : string | undefined {
6+ if ( ! url ) return undefined ;
7+ const username = url . split ( "/" ) . pop ( ) ;
8+ return ( username ?? url ) . startsWith ( "@" ) ? username : `@${ username } ` ;
9+ }
10+
11+ // Get @username from Bluesky URL
12+ function getBlueskyUsername ( url : string ) : string | undefined {
13+ if ( ! url ) return undefined ;
14+ const username = url . split ( "/" ) . pop ( ) ?. replace ( / ^ @ / , "" ) ;
15+ return username ? `@${ username } ` : undefined ;
16+ }
17+
18+ // Get Bluesky profile link from username
19+ function getBlueskyProfileLink ( username : string ) : string {
20+ // Remove any leading @ if present
21+ const cleanUsername = username . replace ( / ^ @ / , "" ) ;
22+ return `https://bsky.app/profile/${ cleanUsername } ` ;
23+ }
24+
25+ // Get @username @instance .tld from Mastodon URL
26+ function getMastodonUsername ( url : string ) : string | undefined {
27+ if ( ! url ) return undefined ;
28+ const match = url . match ( / h t t p s ? : \/ \/ ( [ ^ \/ ] + ) \/ @ ( [ ^ \/ ] + ) ( \/ | \? | $ ) / ) ;
29+ return match ? `@${ match [ 2 ] } @${ match [ 1 ] } ` : undefined ;
30+ }
31+
32+ function getLinkedInUsernameHandler ( url : string ) : string | undefined {
33+ if ( ! url ) return undefined ;
34+ const match = url . match ( / h t t p s ? : \/ \/ ( [ ^ \/ ] + ) \/ i n \/ ( [ ^ \/ ] + ) ( \/ | \? | $ ) / ) ;
35+ if ( match ) {
36+ try {
37+ return `https://www.linkedin.com/in/${ decodeURIComponent ( match [ 2 ] ) } ` ;
38+ } catch {
39+ return `https://www.linkedin.com/in/${ match [ 2 ] } ` ;
40+ }
41+ }
42+ return undefined ;
43+ }
44+
445export const GET : APIRoute = async ( { params, request } ) => {
546 const limit = Infinity ;
647 const speakers = await getCollection ( "speakers" ) ;
7-
848 const exclude = [
949 "sebastian-ramirez" ,
1050 "savannah-ostrowski" ,
1151 "nerea-luis" ,
1252 "petr-baudis" ,
1353 "brett-cannon" ,
1454 ] ;
15-
1655 const records : any [ ] = [ ] ;
1756
1857 const charLimits : Record < string , number > = {
@@ -23,17 +62,31 @@ export const GET: APIRoute = async ({ params, request }) => {
2362 fosstodon : 500 ,
2463 } ;
2564
26- const message_template_full = ( {
27- name,
28- talkTitle,
29- talkUrl,
30- fallbackUrl,
31- } : {
32- name : string ;
33- talkTitle : string ;
34- talkUrl : string ;
35- fallbackUrl : string ;
36- } ) => `Join ${ name } at EuroPython for “${ talkTitle } ”.` ;
65+ // Tailor message templates for each platform using appropriate handle formats
66+ const message_template = {
67+ instagram : ( { name, talkTitle, talkUrl } ) =>
68+ `Join ${ name } at EuroPython for "${ talkTitle } ". Check the link in bio for details!` ,
69+
70+ x : ( { name, handle, talkTitle, talkUrl } ) =>
71+ handle
72+ ? `Join ${ name } (${ handle } ) at EuroPython for "${ talkTitle } ". ${ talkUrl } `
73+ : `Join ${ name } at EuroPython for "${ talkTitle } ". ${ talkUrl } ` ,
74+
75+ linkedin : ( { name, handle, talkTitle, talkUrl } ) =>
76+ handle
77+ ? `Join ${ name } (${ handle } ) at EuroPython for "${ talkTitle } ". ${ talkUrl } `
78+ : `Join ${ name } at EuroPython for "${ talkTitle } ". ${ talkUrl } ` ,
79+
80+ bsky : ( { name, handle, talkTitle, talkUrl } ) =>
81+ handle
82+ ? `Join ${ name } (${ handle } ) at EuroPython for "${ talkTitle } ". ${ talkUrl } `
83+ : `Join ${ name } at EuroPython for "${ talkTitle } ". ${ talkUrl } ` ,
84+
85+ fosstodon : ( { name, handle, talkTitle, talkUrl } ) =>
86+ handle
87+ ? `Join ${ name } (${ handle } ) at EuroPython for "${ talkTitle } ". ${ talkUrl } `
88+ : `Join ${ name } at EuroPython for "${ talkTitle } ". ${ talkUrl } ` ,
89+ } ;
3790
3891 const trimToLimit = ( text : string , limit : number ) =>
3992 text . length <= limit ? text : text . slice ( 0 , limit - 1 ) + "…" ;
@@ -58,37 +111,46 @@ export const GET: APIRoute = async ({ params, request }) => {
58111 const validSessions = sessions . filter (
59112 ( session ) => session && session . data . title
60113 ) ;
114+
61115 if ( validSessions . length === 0 ) continue ;
62116
63117 const talkTitle = validSessions [ 0 ] ?. data . title || "an exciting topic" ;
64118 const talkCode = validSessions [ 0 ] ?. data . code ;
65-
66119 const talkUrl = `https://ep2025.europython.eu/${ talkCode } ` ;
67120 const speakerImage = `https://ep2025-buffer.ep-preview.click/media/social-${ speaker . id } .png` ;
68121 const fallbackUrl = `https://ep2025.europython.eu/speaker/${ speaker . id } ` ;
69- const links = {
70- instagram : fallbackUrl ,
71- x : twitter_url ?? fallbackUrl ,
72- linkedin : linkedin_url ?? fallbackUrl ,
73- bsky : bluesky_url ?? fallbackUrl ,
74- fosstodon : mastodon_url ?? fallbackUrl ,
122+
123+ // Extract handles for each platform
124+ const handles = {
125+ x : getTwitterUsername ( twitter_url || "" ) ,
126+ linkedin : getLinkedInUsernameHandler ( linkedin_url || "" ) ,
127+ bsky : getBlueskyUsername ( bluesky_url || "" ) ,
128+ fosstodon : getMastodonUsername ( mastodon_url || "" ) ,
75129 } ;
76130
77- const generateMessage = ( platform : string ) => {
78- const full = message_template_full ( {
131+ // Generate appropriate messages for each platform
132+ const generateMessage = ( platform : keyof typeof message_template ) => {
133+ const templateFn = message_template [ platform ] ;
134+ const handle =
135+ platform === "instagram"
136+ ? undefined
137+ : handles [ platform as keyof typeof handles ] ;
138+
139+ const full = templateFn ( {
79140 name,
141+ handle,
80142 talkTitle,
81- talkUrl,
82- fallbackUrl : links [ platform ] ,
143+ talkUrl : platform === "instagram" ? fallbackUrl : talkUrl ,
83144 } ) ;
84- const limit = charLimits [ platform ] ;
85145
146+ const limit = charLimits [ platform ] ;
86147 return trimToLimit ( full , limit ) ;
87148 } ;
88149
89150 const record = {
90151 name,
91152 image : speakerImage ,
153+ handles : handles ,
92154 channel : {
93155 instagram : generateMessage ( "instagram" ) ,
94156 x : generateMessage ( "x" ) ,
0 commit comments