Skip to content
Merged
Binary file added public/media/social-abigail-afi-gbadago.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-adrien-cacciaguerra.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-adrin-jalali.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-akshita-gupta.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-alan-franzoni.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-alenka-frim.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-alvaro-duran.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-anastasiia-tymoshchuk.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-anna-schwarz.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-antonio-cuni.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-anupama-tiruvaipati.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-ariane-djeupang.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-arie-bovenberg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-arthur-bayr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-arthur-pastel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-artur-patoka.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-ashish-gupta.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-aya-elsayed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-bojan-miletic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-brett-cannon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-cheuk-ting-ho.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-cristian-maureira-fredes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-david-woods.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-diego-diaz-moron.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/media/social-diego-russo.png
Binary file added public/media/social-dominic-hollis.png
Binary file added public/media/social-eloisa-perez-bennetts.png
Binary file added public/media/social-farhaan-bukhsh.png
Binary file added public/media/social-florian-bruhin.png
Binary file added public/media/social-florian-stefan.png
Binary file added public/media/social-gaffiot-jonathan.png
Binary file added public/media/social-gauden-galea.png
Binary file added public/media/social-gina-haussge.png
Binary file added public/media/social-grzegorz-bokota.png
Binary file added public/media/social-grzegorz-kocjan.png
Binary file added public/media/social-helena-gomez-pozo.png
Binary file added public/media/social-iryna-kondrashchenko.png
Binary file added public/media/social-israel-blancas.png
Binary file added public/media/social-itai-gilo.png
Binary file added public/media/social-iulia-feroli.png
Binary file added public/media/social-jacob-coffee.png
Binary file added public/media/social-jakub-urban.png
Binary file added public/media/social-jan-jasek.png
Binary file added public/media/social-jannis-lubbe.png
Binary file added public/media/social-jenny-vega.png
Binary file added public/media/social-jessica-randall.png
Binary file added public/media/social-jimena-bermudez.png
Binary file added public/media/social-joaquin-rayado.png
Binary file added public/media/social-jochem-van-dooren.png
Binary file added public/media/social-josh-lowe.png
Binary file added public/media/social-juan-luis-cano-rodriguez.png
Binary file added public/media/social-julian-hofer.png
Binary file added public/media/social-julien-lenormand.png
Binary file added public/media/social-kalyan-prasad.png
Binary file added public/media/social-kamil-raczycki.png
Binary file added public/media/social-kamil-tokarski.png
Binary file added public/media/social-karolina-surma.png
Binary file added public/media/social-katerina-scavnicka.png
Binary file added public/media/social-kayode-makinde.png
Binary file added public/media/social-ken-jin.png
Binary file added public/media/social-kir-chou.png
Binary file added public/media/social-kirill-druzhinin.png
Binary file added public/media/social-koteswara-rao-vellanki.png
Binary file added public/media/social-koudai-aono.png
Binary file added public/media/social-krzysztof-wilczynski.png
Binary file added public/media/social-kshitijaa-jaglan.png
Binary file added public/media/social-kumar-anirudha.png
Binary file added public/media/social-lucas-pires.png
Binary file added public/media/social-lysandros-nikolaou.png
Binary file added public/media/social-manivannan-selvaraj.png
Binary file added public/media/social-marc-andre-lemburg.png
Binary file added public/media/social-marco-richetta.png
Binary file added public/media/social-maria-ashna.png
Binary file added public/media/social-marina-moro-lopez.png
Binary file added public/media/social-marine-guyot.png
Binary file added public/media/social-mark-shannon.png
Binary file added public/media/social-martin-christen-1.png
Binary file added public/media/social-martin-christen.png
Binary file added public/media/social-mateusz-modrzejewski.png
Binary file added public/media/social-mateusz-zaremba.png
Binary file added public/media/social-matthieu-amiguet.png
Binary file added public/media/social-matthieu-caneill.png
Binary file added public/media/social-matthieu-rigal.png
Binary file added public/media/social-max-kahan.png
Binary file added public/media/social-may-walter.png
Binary file added public/media/social-mehul-goyal.png
Binary file added public/media/social-michal-szolucha.png
Binary file added public/media/social-miguel-grinberg.png
Binary file added public/media/social-mihail.png
Binary file added public/media/social-mike-muller.png
Binary file added public/media/social-mikulas-poul.png
Binary file added public/media/social-miloslav-pojman.png
Binary file added public/media/social-mykalin-jones.png
Binary file added public/media/social-neeraj-pandey.png
Binary file added public/media/social-nerea-luis.png
Binary file added public/media/social-nicholas-h-tollervey.png
Binary file added public/media/social-nikita-grigorian.png
Binary file added public/media/social-oladapo-kayode-abiodun.png
Binary file added public/media/social-oleh-kostromin.png
Binary file added public/media/social-pablo-galindo-salgado.png
Binary file added public/media/social-padraic-calpin.png
Binary file added public/media/social-paolo-melchiorre.png
Binary file added public/media/social-patrick-hoefler.png
Binary file added public/media/social-pavel-kral.png
Binary file added public/media/social-petr-baudis.png
Binary file added public/media/social-petter-salminen.png
Binary file added public/media/social-ramon-corominas.png
Binary file added public/media/social-raul-cumplido.png
Binary file added public/media/social-reuven-m-lerner.png
Binary file added public/media/social-reyha-verma.png
Binary file added public/media/social-rhythm-patel.png
Binary file added public/media/social-riccardo-magliocchetti.png
Binary file added public/media/social-robson-kanhalelo.png
Binary file added public/media/social-rodrigo-girao-serrao.png
Binary file added public/media/social-rostan-tabet.png
Binary file added public/media/social-ruben-arts.png
Binary file added public/media/social-ruslan-korniichuk.png
Binary file added public/media/social-russell-keith-magee.png
Binary file added public/media/social-samiul-huque.png
Binary file added public/media/social-savannah-ostrowski.png
Binary file added public/media/social-sebastian-buczynski.png
Binary file added public/media/social-sebastian-flores.png
Binary file added public/media/social-sebastian-ramirez.png
Binary file added public/media/social-sebastian-witowski.png
Binary file added public/media/social-sheena.png
Binary file added public/media/social-shivay-lamba.png
Binary file added public/media/social-simon-podhajsky.png
Binary file added public/media/social-sindre-nistad.png
Binary file added public/media/social-stanislav-zmiev.png
Binary file added public/media/social-stefan-barisch.png
Binary file added public/media/social-stepan-sindelar.png
Binary file added public/media/social-steven-kolawole.png
Binary file added public/media/social-sven-arends.png
Binary file added public/media/social-szymon-karpinski.png
Binary file added public/media/social-tal-sofer.png
Binary file added public/media/social-tamara-atanasoska.png
Binary file added public/media/social-tarun-jain.png
Binary file added public/media/social-themistoklis-spanoudis.png
Binary file added public/media/social-theodore-meynard.png
Binary file added public/media/social-tibs.png
Binary file added public/media/social-tomas-roun.png
Binary file added public/media/social-travis-hathaway.png
Binary file added public/media/social-velda-kiara.png
Binary file added public/media/social-vemund-mehl-santi.png
Binary file added public/media/social-vinayak-mehta.png
Binary file added public/media/social-wei-lee.png
Binary file added public/media/social-yuliia-barabash.png
170 changes: 170 additions & 0 deletions src/pages/api/buffer_posts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { getCollection, getEntry } from "astro:content";
import type { APIRoute } from "astro";

// Get @username from Twitter URL
function getTwitterUsername(url: string): string | undefined {
if (!url) return undefined;
const username = url.split("/").pop();
return (username ?? url).startsWith("@") ? username : `@${username}`;
}

// Get @username from Bluesky URL
function getBlueskyUsername(url: string): string | undefined {
if (!url) return undefined;
const username = url.split("/").pop()?.replace(/^@/, "");
return username ? `@${username}` : undefined;
}

// Get Bluesky profile link from username
function getBlueskyProfileLink(username: string): string {
// Remove any leading @ if present
const cleanUsername = username.replace(/^@/, "");
return `https://bsky.app/profile/${cleanUsername}`;
}

// Get @[email protected] from Mastodon URL
function getMastodonUsername(url: string): string | undefined {
if (!url) return undefined;
const match = url.match(/https?:\/\/([^\/]+)\/@([^\/]+)(\/|\?|$)/);
return match ? `@${match[2]}@${match[1]}` : undefined;
}

function getLinkedInUsernameHandler(url: string): string | undefined {
if (!url) return undefined;
const match = url.match(/https?:\/\/([^\/]+)\/in\/([^\/]+)(\/|\?|$)/);
if (match) {
try {
return `https://www.linkedin.com/in/${decodeURIComponent(match[2])}`;
} catch {
return `https://www.linkedin.com/in/${match[2]}`;
}
}
return undefined;
}

export const GET: APIRoute = async ({ params, request }) => {
const limit = Infinity;
const speakers = await getCollection("speakers");
const exclude = [
"sebastian-ramirez",
"savannah-ostrowski",
"nerea-luis",
"petr-baudis",
"brett-cannon",
];
const records: any[] = [];

const charLimits: Record<string, number> = {
instagram: 2200,
x: 280,
linkedin: 3000,
bsky: 300,
fosstodon: 500,
};

// Tailor message templates for each platform using appropriate handle formats
const message_template = {
instagram: ({ name, talkTitle, talkUrl }) =>
`Join ${name} at EuroPython for "${talkTitle}".`,

x: ({ name, handle, talkTitle, talkUrl }) =>
handle
? `Join ${name} (${handle}) at EuroPython for "${talkTitle}". Talk: ${talkUrl}`
: `Join ${name} at EuroPython for "${talkTitle}". Talk: ${talkUrl}`,

linkedin: ({ name, handle, talkTitle, talkUrl }) =>
`Join ${name} at EuroPython for "${talkTitle}".`,

bsky: ({ name, handle, talkTitle, talkUrl }) =>
handle
? `Join ${name} (${handle}) at EuroPython for "${talkTitle}". Talk: ${talkUrl}`
: `Join ${name} at EuroPython for "${talkTitle}". Talk: ${talkUrl}`,

fosstodon: ({ name, handle, talkTitle, talkUrl }) =>
handle
? `Join ${name} (${handle}) at EuroPython for "${talkTitle}". talk: ${talkUrl}`
: `Join ${name} at EuroPython for "${talkTitle}". Talk: ${talkUrl}`,
};

const trimToLimit = (text: string, limit: number) =>
text.length <= limit ? text : text.slice(0, limit - 1) + "…";

for (const speaker of speakers) {
if (records.length >= limit) break;
if (exclude.includes(speaker.id)) continue;

const {
name,
twitter_url,
linkedin_url,
bluesky_url,
mastodon_url,
submissions,
} = speaker.data;

const sessions = await Promise.all(
submissions.map((session) => getEntry("sessions", session.id))
);

const validSessions = sessions.filter(
(session) => session && session.data.title
);

if (validSessions.length === 0) continue;

const talkTitle = validSessions[0]?.data.title || "an exciting topic";
const talkCode = validSessions[0]?.data.code;
const talkUrl = `https://ep2025.europython.eu/${talkCode}`;
const speakerImage = `https://ep2025-buffer.ep-preview.click/media/social-${speaker.id}.png`;
const fallbackUrl = `https://ep2025.europython.eu/speaker/${speaker.id}`;

// Extract handles for each platform
const handles = {
x: getTwitterUsername(twitter_url || ""),
linkedin: getLinkedInUsernameHandler(linkedin_url || ""),
bsky: getBlueskyUsername(bluesky_url || ""),
fosstodon: getMastodonUsername(mastodon_url || ""),
};

// Generate appropriate messages for each platform
const generateMessage = (platform: keyof typeof message_template) => {
const templateFn = message_template[platform];
const handle =
platform === "instagram"
? undefined
: handles[platform as keyof typeof handles];

const full = templateFn({
name,
handle,
talkTitle,
talkUrl: platform === "instagram" ? fallbackUrl : talkUrl,
});

const limit = charLimits[platform];
return trimToLimit(full, limit);
};

const record = {
name,
image: speakerImage,
handles: handles,
channel: {
instagram: generateMessage("instagram"),
x: generateMessage("x"),
linkedin: generateMessage("linkedin"),
bsky: generateMessage("bsky"),
fosstodon: generateMessage("fosstodon"),
},
};

records.push(record);
}

return new Response(JSON.stringify(records, null, 2), {
status: 200,
headers: {
"Content-Type": "application/json",
},
});
};
78 changes: 78 additions & 0 deletions src/pages/media/social_media.csv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { getCollection, getEntry } from "astro:content";
export async function GET({ params, request }) {
const speakers = await getCollection("speakers");

const header = [
"Talk Title",
"Speaker Name",
"Speaker Photo URL",
"Primary Social ULR",
"X URL",
"LinkedIn URL",
"Bluesky URL",
"Mastodon URL",
];

const exclude = [
"sebastian-ramirez",
"savannah-ostrowski",
"nerea-luis",
"petr-baudis",
"brett-cannon",
];

const rows: string[][] = [];

for (const speaker of speakers) {
if (exclude.includes(speaker.id)) continue;

const {
name,
twitter_url,
linkedin_url,
bluesky_url,
mastodon_url,
submissions,
} = speaker.data;

const sessions = await Promise.all(
submissions.map((session) => getEntry("sessions", session.id))
);

for (const session of sessions) {
if (session) {
const speaker_page = `https://ep2025.europython.eu/speaker/${speaker.id}`;
rows.push([
session.data.title || "",
name,
`https://ep2025-buffer.ep-preview.click/media/social-${speaker.id}.png`,
twitter_url || linkedin_url || mastodon_url || speaker_page,
twitter_url ?? speaker_page,
linkedin_url ?? speaker_page,
bluesky_url ?? speaker_page,
mastodon_url ?? speaker_page,
]);
}
}
}

const csvLines = [header, ...rows]
.map((row) =>
row
.map((field) =>
field.includes('"') || field.includes(",") || field.includes("\n")
? `"${field.replace(/"/g, '""')}"`
: field
)
.join(",")
)
.join("\r\n");

return new Response(csvLines, {
status: 200,
headers: {
"Content-Type": "text/csv; charset=utf-8",
"Content-Disposition": 'attachment; filename="social_media.csv"',
},
});
}