Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a0fb023
update socials queue
celinaczy May 25, 2026
0608135
add tiktok channel, talk/tutorial label, and per-session cards
celinaczy Jun 3, 2026
264654e
include all session types in speaker cards (poster, summit → session …
celinaczy Jun 3, 2026
891e11b
add talk link to linkedin speaker message
celinaczy Jun 3, 2026
242ea92
per-session speaker cards: separate images per talk, update generatio…
celinaczy Jun 3, 2026
ef229e9
updated queue (changed after PyGda which is already scheduled)
celinaczy Jun 3, 2026
83caef9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 3, 2026
452353f
update docs
celinaczy Jun 3, 2026
336d49f
Merge branch 'ep2026-socials-scripts-update' of https://github.com/Eu…
celinaczy Jun 3, 2026
a29fdb5
Merge branch 'ep2026' into ep2026-socials-scripts-update
celinaczy Jun 3, 2026
768ee80
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 3, 2026
1217d90
Merge branch 'ep2026' into ep2026-socials-scripts-update
celinaczy Jun 4, 2026
baccbe3
reorder queue to have the same pattern as before (speaker, speaker, s…
celinaczy Jun 7, 2026
3d9848b
add 'get your ticket' CTA
celinaczy Jun 7, 2026
642ebaa
Merge branch 'ep2026-socials-scripts-update' of https://github.com/Eu…
celinaczy Jun 7, 2026
d3553c1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 7, 2026
49b71e6
Merge branch 'ep2026' into ep2026-socials-scripts-update
celinaczy Jun 7, 2026
f5edf24
update docs
celinaczy Jun 7, 2026
d464f25
Merge branch 'ep2026' into ep2026-socials-scripts-update
celinaczy Jun 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 61 additions & 8 deletions docs/social-media-scheduling.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ screenshotted with Puppeteer.

### Speaker cards

Speaker cards are generated per-session. Speakers with multiple qualifying
sessions (Talk, Tutorial, or other session types) get one card per session,
named `social-${slug}-${sessionCode}.png`. Single-session speakers get the
standard `social-${slug}.png`.

```bash
node scripts/download_social_speakers.cjs
```
Expand All @@ -59,6 +64,11 @@ Screenshots are saved to the current directory. Move them to the right place:
mv social-*.png public/media/speakers/
```

When regenerating, skip files that already exist to avoid overwriting images for
speakers whose posts are already scheduled. If a speaker previously had one
session and now has a second, delete the old generic `social-${slug}.png` after
the per-session images are committed.

### Sponsor & partner cards

```bash
Expand Down Expand Up @@ -102,8 +112,33 @@ Regenerate it by hitting the API endpoint while the dev server is running:
curl -s http://localhost:4321/api/media/combined_socials_queue > src/pages/api/media/combined_socials_queue.json
```

This overwrites `src/pages/api/media/combined_socials_queue.json` with all 150+
items sorted and interleaved.
This overwrites `src/pages/api/media/combined_socials_queue.json` with all items
sorted and interleaved.

> **Note:** There are two queue files:
>
> - `combined_socials_queue.json` — the freshly generated queue, used as the
> source of truth and read by `buffer-scheduling.py`
> - `combined_socials_queue_2026.json` — the manually curated queue that
> preserves already-scheduled posts at the top; new entries are appended after
> the last scheduled position

When new speakers or sponsors are added, regenerate
`combined_socials_queue.json` and then merge the new entries into
`combined_socials_queue_2026.json` manually — keeping already-posted entries
intact and appending only new/unposted ones.

### Session types and labels

The queue includes all qualifying session types — not just talks and tutorials.
The label used in post text is determined by session type:

- `"Talk"` / `"Talk (long session)"` → `"talk"`
- `"Tutorial"` → `"tutorial"`
- Everything else (Poster, Summit, etc.) → `"session"`

Speakers with multiple qualifying sessions get one queue entry per session, each
with its own per-session card image.

### Tier classification

Expand Down Expand Up @@ -238,23 +273,41 @@ python src/pages/api/media/buffer-scheduling.py
### Channel notes

- **Instagram**: requires `postType: post` — handled automatically
- **Sponsors/partners**: no Instagram channel (only x, linkedin, bsky,
- **TikTok**: supports photo posts with an optional `title` (set to the speaker
name) — handled automatically
- **Sponsors/partners**: no Instagram or TikTok channel (only x, linkedin, bsky,
fosstodon)
- **Speakers**: all 5 channels including Instagram
- **Speakers**: all 6 channels including Instagram and TikTok
- **Bluesky**: Buffer returns this as `"bluesky"` — normalized to `"bsky"`
automatically
- **Ticket call-to-action**: speaker posts include a "Get your ticket" line on
Instagram, LinkedIn, Fosstodon, and TikTok. X and Bluesky are skipped due to
character limits. Instagram and TikTok use the short form
(`europython.eu/tickets/`) since URLs are not clickable on those platforms;
LinkedIn and Fosstodon use the full clickable
`https://europython.eu/tickets/`.

### Tracking progress

After each successful run, note the last `QUEUE_END` value. The next run should
set `QUEUE_START = previous QUEUE_END + 1`.

Already scheduled as of initial setup:
The script reads from `combined_socials_queue.json` by default. When switching
to the curated `combined_socials_queue_2026.json`, update `queue_path` in the
script accordingly.

Already scheduled as of June 2026:

- Positions 1–3: Abhik Sarkar, Abhimanyu Singh Shekhawat, Abigail Afi Gbadago
- Positions 1–67 in `combined_socials_queue_2026.json` (up to and including
PyGda partner post)
- Cheuk Ting Ho and Diego Russo were posted manually outside the queue. Their
per-session cards (`social-cheuk-ting-ho-VYUNHG.png`,
`social-cheuk-ting-ho-WKBHZW.png`, `social-diego-russo-QXZMP8.png`,
`social-diego-russo-W9NLXV.png`) are committed for consistency so all speaker
cards are available on the site, even though they aren't part of the active
queue rotation.

Next batch (positions 4–8): Adam Gorgoń, Alejandro Cabello Jiménez,
ActiveCampaign, Aleksander, 1Password
Next batch starts at position 68 (Grzegorz Kocjan).

---

Expand Down
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 removed public/media/speakers/social-cheuk-ting-ho.png
Binary file not shown.
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 removed public/media/speakers/social-diego-russo.png
Binary file not shown.
Binary file added public/media/speakers/social-gyeongjae-choi.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/speakers/social-hood-chatham.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/speakers/social-jannis-leidel.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.
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 not shown.
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 not shown.
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 not shown.
11 changes: 8 additions & 3 deletions scripts/download_social_speakers.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,16 @@ const puppeteer = require("puppeteer");
for (let i = 0; i < elements.length; i++) {
const el = elements[i];

// Get the slug from the element
// Get the slug and optional session code from the element
const slug = await page.evaluate((el) => el.getAttribute("data-slug"), el);
const code = await page.evaluate((el) => el.getAttribute("data-code"), el);

// Fallback if slug is missing
const filename = slug ? `social-${slug}.png` : `social-${i}.png`;
// Use session code suffix when present (multi-session speaker)
const filename = code
? `social-${slug}-${code}.png`
: slug
? `social-${slug}.png`
: `social-${i}.png`;

await el.screenshot({ path: filename });
console.log(`Saved ${filename}`);
Expand Down
7 changes: 5 additions & 2 deletions src/components/SocialMediaCard.astro
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
---
import { getEntries, type CollectionEntry } from "astro:content";
const { entry } = Astro.props;
const { entry, session } = Astro.props;
const sessions: CollectionEntry<"sessions">[]= await getEntries(entry.data.submissions);
// If a specific session is passed use it, otherwise fall back to all (legacy)
const sessions: CollectionEntry<"sessions">[] = session
? [session]
: await getEntries(entry.data.submissions);
---
<svg class="bg" width="900" height="900" class="h-full w-full -z-10">
<image href="/social/speaker_card.png" width="900" height="900" />
Expand Down
5 changes: 3 additions & 2 deletions src/pages/api/media/buffer-scheduling.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,9 @@
"text": text,
"schedulingType": "automatic",
"mode": "addToQueue",
"assets": [{"image": {"url": item["image"], "alt_text": item.get("alt_text", "")}}] if item.get("image") else [],
**({"metadata": {"instagram": {"type": "post", "shouldShareToFeed": True}}} if network == "instagram" else {})
"assets": [{"image": {"url": item["image"], "metadata": {"altText": item.get("alt_text", "")}}}] if item.get("image") else [],
**({"metadata": {"instagram": {"type": "post", "shouldShareToFeed": True}}} if network == "instagram" else {}),
**({"metadata": {"tiktok": {"title": item["name"]}}} if network == "tiktok" else {})
}
}

Expand Down
Loading
Loading