Moderní tvorba webových aplikací

O webu

Automatické sdílení článků na X a Facebook přes GitHub Actions

Jak nastavit GitHub Actions workflow pro automatické publikování nových článků na sociální sítě po deployi.

7 minut

Publikujete článek a pak ho ručně sdílíte na X, Facebook, LinkedIn? Tenhle proces lze snadno automatisovat pomocí GitHub Actions. Po každém deployi se nový článek automaticky publikuje na sociální sítě.

Předpoklady

Toto řešení předpokládá, že obsah webu spravujete přes Git. Články jsou uložené jako soubory v repozitáři (např. Markdown v content/posts/) a publikování probíhá přes push do main branch. Workflow detekuje nové články pomocí git diff, takže bez Gitu to nefunguje.

Nejpracnější část celého nastavení je získání API klíčů od jednotlivých platforem. X i Facebook mají komplikovaná vývojářská rozhraní, kde se člověk snadno ztratí. Počítejte s tím, že proklikávání formulářů, nastavování oprávnění a generování tokenů zabere víc času než samotné psaní workflow.

Jak to funguje

Workflow se spouští po úspěšném Vercel deployi pomocí deployment_status eventu:

  1. Vercel dokončí deploy a pošle status do GitHubu
  2. GitHub Actions workflow detekuje nové články pomocí git diff
  3. Pro každý nový článek zavolá API sociálních sítí
  4. Uloží seznam publikovaných článků, aby se předešlo duplicitám

Základní workflow

name: Share New Articles on Social Media

on:
  deployment_status:

jobs:
  share-articles:
    runs-on: ubuntu-latest
    if: |
      github.event.deployment_status.state == 'success' &&
      github.event.deployment.environment == 'Production'
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - uses: pnpm/action-setup@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Detect new articles
        id: detect
        run: |
          node scripts/social/get-new-articles.js --json > new-articles.json
          ARTICLE_COUNT=$(node -e "console.log(require('./new-articles.json').articles.length)")
          echo "article_count=$ARTICLE_COUNT" >> $GITHUB_OUTPUT
          echo "has_articles=$([[ $ARTICLE_COUNT -gt 0 ]] && echo true || echo false)" >> $GITHUB_OUTPUT

      - name: Post to X (Twitter)
        if: steps.detect.outputs.has_articles == 'true'
        env:
          X_API_KEY: ${{ secrets.X_API_KEY }}
          X_API_SECRET: ${{ secrets.X_API_SECRET }}
          X_ACCESS_TOKEN: ${{ secrets.X_ACCESS_TOKEN }}
          X_ACCESS_TOKEN_SECRET: ${{ secrets.X_ACCESS_TOKEN_SECRET }}
        run: node scripts/social/post-to-x.js

      - name: Post to Facebook
        if: steps.detect.outputs.has_articles == 'true'
        env:
          FACEBOOK_PAGE_ID: ${{ secrets.FACEBOOK_PAGE_ID }}
          FACEBOOK_ACCESS_TOKEN: ${{ secrets.FACEBOOK_ACCESS_TOKEN }}
        run: node scripts/social/post-to-facebook.js

Detekce nových článků

Klíčem je správně detekovat pouze nově přidané články, ne upravené. Používáme git diff s filtrem --diff-filter=A (Added):

git diff --name-only --diff-filter=A HEAD~1..HEAD -- 'content/posts/*.md'

Tím získáme seznam souborů, které byly přidány v posledním commitu. Důležité je fetch-depth: 2 v checkout action, jinak nemáme historii pro porovnání.

Nastavení X (Twitter) API

X používá OAuth 1.0a, což vyžaduje 4 klíče:

  1. Vytvořte aplikaci na X Developer Portal
  2. V User authentication settings nastavte Read and write oprávnění
  3. Vygenerujte API Key, API Secret, Access Token a Access Token Secret

Důležité: Access Token musí být vygenerován po nastavení Read and write oprávnění. Pokud změníte oprávnění, musíte token regenerovat.

OAuth 1.0a signature

X API vyžaduje podepsané požadavky. Signature se generuje takto:

function generateOAuthSignature(method, url, params, consumerSecret, tokenSecret) {
  const sortedParams = Object.keys(params)
    .sort()
    .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
    .join('&');

  const signatureBaseString = [
    method.toUpperCase(),
    encodeURIComponent(url),
    encodeURIComponent(sortedParams)
  ].join('&');

  const signingKey = `${encodeURIComponent(consumerSecret)}&${encodeURIComponent(tokenSecret)}`;

  return crypto.createHmac('sha1', signingKey)
    .update(signatureBaseString)
    .digest('base64');
}

Nastavení Facebook API

Facebook je jednodušší - stačí Page Access Token:

  1. Vytvořte aplikaci na Facebook Developers
  2. V Graph API Explorer vygenerujte token s oprávněním pages_manage_posts
  3. Převeďte na dlouhodobý token (viz níže)

Permanentní Page Access Token

Krátkodobý token vyprší za pár hodin. Pro automatisaci potřebujete permanentní:

# 1. Převeďte user token na dlouhodobý (60 dní)
GET /oauth/access_token?
  grant_type=fb_exchange_token&
  client_id={APP_ID}&
  client_secret={APP_SECRET}&
  fb_exchange_token={SHORT_TOKEN}

# 2. Získejte permanentní page token
GET /{PAGE_ID}?fields=access_token&access_token={LONG_LIVED_USER_TOKEN}

Výsledný token nevyprší, dokud nezměníte heslo nebo neodeberete oprávnění aplikaci.

Publikování na Facebook

const response = await fetch(
  `https://graph.facebook.com/v19.0/${pageId}/feed`,
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      message: 'Text příspěvku',
      link: 'https://example.com/clanek',
      access_token: pageAccessToken
    })
  }
);

Prevence duplicit

Při re-runu workflow by se článek publikoval znovu. Řešením je tracking soubor:

// .github/shared-articles.json
{
  "articles": ["slug-clanku-1", "slug-clanku-2"]
}

Před publikováním zkontrolujeme, zda článek už není v seznamu:

function getSharedArticles() {
  const filePath = '.github/shared-articles.json';
  if (!fs.existsSync(filePath)) return new Set();

  const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
  return new Set(data.articles || []);
}

// Filtrování nových článků
const sharedArticles = getSharedArticles();
const newArticles = articles.filter(a => !sharedArticles.has(a.slug));

Po úspěšném publikování přidáme slug do souboru a commitneme:

- name: Commit shared articles tracking
  run: |
    git config user.name "github-actions[bot]"
    git config user.email "github-actions[bot]@users.noreply.github.com"
    git add .github/shared-articles.json
    git diff --staged --quiet || git commit -m "chore: mark articles as shared [skip ci]"
    git push

Důležitý je [skip ci] v commit message - zabrání spuštění dalšího workflow a Vercel rebuildu.

GitHub Secrets

Všechny API klíče uložte v Settings → Secrets and variables → Actions:

SecretPopis
X_API_KEYX API Key (Consumer Key)
X_API_SECRETX API Secret
X_ACCESS_TOKENX Access Token (s Read+Write)
X_ACCESS_TOKEN_SECRETX Access Token Secret
FACEBOOK_PAGE_IDID vaší Facebook stránky
FACEBOOK_ACCESS_TOKENPermanentní Page Access Token

Debugging

Nejčastější chyby:

  • X 401 Unauthorized - Access Token má špatná oprávnění. Zkontrolujte, že je "Read and write" a regenerujte token.
  • Facebook (#200) Permissions error - Token nemá oprávnění pages_manage_posts.
  • Článek se nepublikuje - Zkontrolujte, že fetch-depth: 2 je nastaveno a článek má status: 1.

Pro lokální testování:

# X
export X_API_KEY="..." X_API_SECRET="..." X_ACCESS_TOKEN="..." X_ACCESS_TOKEN_SECRET="..."
node scripts/social/post-to-x.js --article='{"title":"Test","url":"https://example.com","tags":["test"]}'

# Facebook
export FACEBOOK_PAGE_ID="..." FACEBOOK_ACCESS_TOKEN="..."
node scripts/social/post-to-facebook.js --article='{"title":"Test","url":"https://example.com"}'

Rozšíření

Stejný princip lze použít pro LinkedIn, Mastodon nebo jiné platformy. Stačí přidat další krok do workflow a odpovídající skript pro volání API.

Novinky e-mailem

Když budu mít něco opravdu zajímavého, můžu vám to poslat e-mailem

Přidej se k 500+ čtenářům
Jen kvalitní obsah
Žádný spam

Web jecas.cz píše Bohumil Jahoda, kontakt
Seznam všech článků
2013–2026