Skip to content

Social media automation: data sources and assets. #1215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: ep2025
Choose a base branch
from
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 @username@instance.tld 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",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Primary Social ULR",
"Primary Social URL",

"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"',
},
});
}