Bagaimana saya mengotomatiskan buletin Substack saya dengan konten dari blog saya

Bagaimana saya mengotomatiskan buletin Substack saya dengan konten dari blog saya


Bagaimana saya mengotomatiskan buletin Substack saya dengan konten dari blog saya

19 November 2025

Saya mengirimkan buletin Substack mingguan saya pagi ini dan mengambil kesempatan untuk merekam video YouTube yang menunjukkan proses saya dan menjelaskan berbagai komponen yang membuatnya berfungsi. Ada sebuah banyak dari lakban digital yang terlibat, mengambil konten dari Django+Heroku+PostgreSQL ke GitHub Actions ke SQLite+Datasette+Fly.io ke JavaScript+Observable dan akhirnya ke Substack.

Proses intinya sama seperti yang saya jelaskan pada tahun 2023. Saya memiliki buku catatan Observable bernama blog-to-newsletter yang mengambil konten dari database blog saya, memfilter apa pun yang telah ada di buletin sebelumnya, memformat apa yang tersisa sebagai HTML dan menawarkan tombol besar “Salin buletin teks kaya ke papan klip”.

Tangkapan layar antarmuka. Sebuah item dalam daftar mengatakan 9080: Mencoba Gemini 3 Pro dengan transkripsi audio dan benchmark pelican baru. Tombol besar bertuliskan Salin buletin teks kaya ke papan klip - di bawah adalah tombol kecil yang bertuliskan Salin tautan/kutipan/TIL saja. Penggeser X hari terakhir disetel ke 2. Terdapat kotak centang untuk konten SKip yang dikirim dalam buletin sebelumnya dan hanya menyertakan konten postingan sebelum batas komentar.

Saya mengklik tombol itu, menempelkan hasilnya ke editor Substack, mengubah beberapa hal dan menekan kirim. Keseluruhan proses biasanya hanya memakan waktu beberapa menit.

Saya melakukan sedikit pengeditan:

  • Saya menetapkan judul dan subjudul untuk buletin. Ini sering kali merupakan salinan langsung dari judul postingan blog unggulan.
  • Substack mengubah URL YouTube menjadi sematan, yang sering kali tidak saya inginkan—terutama jika saya memiliki URL YouTube di dalam contoh kode.
  • Blok teks yang telah diformat sebelumnya sering kali memiliki baris kosong tambahan di bagian akhir, yang saya hapus.
  • Kadang-kadang saya akan melakukan pengeditan konten—menghapus sebagian konten yang tidak sesuai dengan buletin, atau memperbaiki referensi waktu seperti “kemarin” yang tidak masuk akal lagi.
  • Saya memilih gambar unggulan untuk buletin dan menambahkan beberapa tag.

Itulah keseluruhan prosesnya!

Buku catatan yang Dapat Diamati

Sel terpenting dalam buku catatan Observable adalah yang ini:

raw_content = {
  return await (
    await fetch(
      `https://datasette.simonwillison.net/simonwillisonblog.json?sql=${encodeURIComponent(
        sql
      )}&_shape=array&numdays=${numDays}`
    )
  ).json();
}

Ini menggunakan JavaScript fetch() berfungsi untuk mengambil data dari contoh Datasette blog saya, menggunakan kueri SQL yang sangat kompleks yang dibuat di tempat lain di buku catatan.

Berikut tautan untuk melihat dan menjalankan kueri tersebut langsung di Kumpulan Data. Ini adalah 143 baris SQL berbelit-belit yang merakit sebagian besar HTML untuk buletin menggunakan rangkaian string SQLite! Cuplikan ilustrasi:

with content as (
  select
    id,
    'entry' as type,
    title,
    created,
    slug,
    '

'

|| 'https://simonwillison.net/' || strftime('%Y/', created) || substr('JanFebMarAprMayJunJulAugSepOctNovDec', (strftime('%m', created) - 1) * 3 + 1, 3) || '/' || cast(strftime('%d', created) as integer) || '/' || slug || '/' || '">' || title || ' - ' || date(created) || '' || body as html, 'null' as json, '' as external_url from blog_entry union all # ...

URL blog saya terlihat seperti ini /2025/Nov/18/gemini-3/—SQL ini membuat singkatan tiga huruf bulan dari nomor bulan menggunakan operasi substring.

Ini adalah sebuah sangat buruk cara merakit HTML, tapi saya terjebak karena itu menghibur saya.

Buku catatan Observable lainnya mengambil data tersebut, memfilter apa pun yang tertaut ke konten yang disebutkan dalam buletin sebelumnya dan menyusunnya menjadi blok HTML yang dapat disalin menggunakan tombol besar itu.

Berikut resep yang digunakan untuk mengubah HTML menjadi konten teks kaya pada clipboard yang cocok untuk Substack. Saya tidak ingat bagaimana saya menemukan jawabannya tetapi ini sangat efektif:

Object.assign(
  html`<button style="https://simonwillison.net/2025/Nov/19/how-i-automate-my-substack-newsletter/font-size: 1.4em; padding: 0.3em 1em; font-weight: bold;"https://simonwillison.net/2025/Nov/19/how-i-automate-my-substack-newsletter/>Copy rich text newsletter to clipboard`,
  {
    onclick: () => {
      const htmlContent = newsletterHTML;
      // Create a temporary element to hold the HTML content
      const tempElement = document.createElement("div");
      tempElement.innerHTML = htmlContent;
      document.body.appendChild(tempElement);
      // Select the HTML content
      const range = document.createRange();
      range.selectNode(tempElement);
      // Copy the selected HTML content to the clipboard
      const selection = window.getSelection();
      selection.removeAllRanges();
      selection.addRange(range);
      document.execCommand("copy");
      selection.removeAllRanges();
      document.body.removeChild(tempElement);
    }
  }
)

Dari Django+Postgresql ke Kumpulan Data+SQLite

Blog saya sendiri adalah aplikasi Django yang dihosting di Heroku, dengan data disimpan di Heroku PostgreSQL. Berikut kode sumber untuk aplikasi Django itu. Saya menggunakan admin Django sebagai CMS saya.

Datasette menyediakan API JSON melalui database SQLite… yang berarti ada sesuatu yang perlu mengubah database PostgreSQL tersebut menjadi database SQLite yang dapat digunakan oleh Datasette.

Sistem saya untuk melakukan hal itu ada di repositori GitHub simonw/simonwillisonblog-backup. Ia menggunakan Tindakan GitHub pada jadwal yang dijalankan setiap dua jam, mengambil data terbaru dari PostgreSQL dan mengonversinya ke SQLite.

Alat db-to-sqlite saya bertanggung jawab atas konversi itu. Saya menyebutnya seperti ini:

db-to-sqlite \
  $(heroku config:get DATABASE_URL -a simonwillisonblog | sed s/postgres:/postgresql+psycopg2:/) \
  simonwillisonblog.db \
  --table auth_permission \
  --table auth_user \
  --table blog_blogmark \
  --table blog_blogmark_tags \
  --table blog_entry \
  --table blog_entry_tags \
  --table blog_quotation \
  --table blog_quotation_tags \
  --table blog_note \
  --table blog_note_tags \
  --table blog_tag \
  --table blog_previoustagname \
  --table blog_series \
  --table django_content_type \
  --table redirects_redirect

Itu heroku config:get DATABASE_URL perintah menggunakan kredensial Heroku dalam variabel lingkungan untuk mengambil URL koneksi database untuk database PostgreSQL blog saya (dan memperbaiki perbedaan kecil dalam skema URL).

db-to-sqlite kemudian dapat mengekspor data itu dan menulisnya ke file database SQLite bernama simonwillisonblog.db.

Itu --table opsi menentukan tabel yang harus disertakan dalam ekspor.

Repositori melakukan lebih dari sekedar konversi: ia juga mengekspor data yang dihasilkan ke file JSON yang ada di repositori, yang memberi saya riwayat penerapan perubahan yang saya buat pada konten saya. Ini adalah cara murah untuk mendapatkan riwayat revisi konten blog saya tanpa harus dipusingkan dengan pelacakan riwayat terperinci di dalam aplikasi Django itu sendiri.

Di akhir alur kerja Tindakan GitHub saya adalah kode ini yang menerbitkan database yang dihasilkan ke Kumpulan Data yang berjalan di Fly.io menggunakan plugin publikasikan kumpulan data fly:

datasette publish fly simonwillisonblog.db \
  -m metadata.yml \
  --app simonwillisonblog-backup \
  --branch 1.0a2 \
  --extra-options "--setting sql_time_limit_ms 15000 --setting truncate_cells_html 10000 --setting allow_facet off" \
  --install datasette-block-robots \
  # ... more plugins

Seperti yang Anda lihat, ada banyak bagian yang bergerak! Anehnya, sebagian besar semuanya berhasil—saya jarang perlu campur tangan dalam prosesnya, dan biaya untuk berbagai komponen tersebut cukup rendah.



Source link

Postagens Similares

  • サイボーグ vs 部屋、コンピューティングの未来に対する 2 つのビジョン (相互接続)

    大まかに言うと、私たちがコンピューターと対話する方法の将来については、サイボーグと部屋という 2 つのビジョンがあると考えています。 1 つ目は、業界が今日どこに向かっているのかということです。私は後者のほうに興味があります。 サイボーグ 短期的には、サイボーグとはウェアラブルを意味します。 1960 年にクラインズとクラインによってサイボーグの最初の定義は次のとおりでした。 人間が自分の体を新しい環境に適応させること (前述したとおり)。 Apple AirPods はサイボーグ機能を強化したものです。透明モードでは聞こえが良くなります。 メタ AI メガネは、記憶力とインターネットの知識を強化します。質問をつぶやくと、その答えが音声で返され、作業記憶にサイドローディングされます。認知的には、これは何かを思い出すために一生懸命考えるのと同じように感じられます。 方向感覚を与えてくれるスマートウォッチ、バイオフィードバック用のスマートリング、完璧な想起と予測のためのスマートなイヤホンとメガネが備わる未来が構築されつつあるのが目に見えています…アンディ・クラークの『Natural Born Cyborgs』(2003年)では、なぜこれがすでに私たちの脳の働きと完全にインピーダンス整合しているのかを説明しています。 長期ですか?私は以前、足を歩いて通勤できる経頭蓋磁気刺激ヘルメットについて冗談を言いましたが、これはサイボーグの移動方向です。向知性薬、CRISPR遺伝子治療、肉体改造、そして指先を切り開いて磁石を挿入して電場第六感を得るというものです。 しかし、ラペルピン、ストラップ、リング、ニューラリンク、その他のブレイン コンピューター インターフェイスなど、未来の AI ネイティブ フォーム ファクターを作ろうとしているハードウェア スタートアップ企業で、サイボーグ パラダイムが実際に動作しているのがわかります… テクノロジー企業がサード デバイス (PC やスマートフォンに次ぐ神話上のデバイス) について考えるとき、彼らが到達するのはこれです。パーソナル コンピューターの未来は、人間をコンピューターに変えることです。 客室 拡張されたユーザーと拡張された環境を対比します。特に: Dynamicland (2018) – ブレット・ビクターのビジョン 場所であるコンピューター、 プログラム可能な部屋 Put-that-there (1980) – ルームスケールのマルチモーダル (音声とジェスチャー) 会話コンピューティングに関する MIT の研究 プロジェクト Cyber​​syn (1971)…

  • Емма Луїза знаходить внутрішню силу в “Сонце для щастя”

    У своєму шостому альбомі «Sunshine for Happiness» Емма Луїза вчить слухачів, як боротися зі складними емоціями, стверджуючи, що люди завжди можуть рухатися далі, якщо вони зосереджуються на тому, що має значення. Потік: «Сонце для щастя» – Емма Луїза Вколи Емма Луїза молиться, це для себе. У її співах все ще є духовний елемент, слова,…

  • Strategie comprovate per ottenere la citazione del tuo marchio

    Quando i clienti fanno una domanda all’IA, solo una manciata di fonti vengono citate nella risposta. Quali segnali di contenuto valuta l’intelligenza artificiale quando seleziona le fonti da citare? I contenuti del tuo brand sono strutturati per essere uno di questi? Questa non è più una questione tecnologica; è una questione di strategia del marchio…

  • Musik zum Abheben – Unruhe

    Jeden Donnerstag wird den Gruppenmitgliedern in der Musikgemeinschaft Disquiet Junto eine neue kompositorische Herausforderung gestellt, die dann fünf Tage Zeit hat, einen Titel als Reaktion auf die Projektanweisungen aufzunehmen und hochzuladen. Die Mitgliedschaft im Junto ist offen: Einfach beitreten und mitmachen. (Ein SoundCloud-Konto ist hilfreich, aber nicht erforderlich.) Es besteht kein Druck, jedes Projekt durchführen…

  • Saya suka AI slop dan saya tidak bisa berbohong (Terhubung)

    Saya mencari direktori home saya di desktop Mac saya, yang jarang saya lakukan (saya menjalankan operasi yang rapi di sini), dan saya menemukan file yang tidak saya kenal bernama keluar.html. Ini keluar.html. Untuk kepentingan rekaman itu: ini adalah beranda bergaya GeoCities setengah matang lengkap dengan puisi favorit, karakter rusak, dan a Halaman ini paling baik…

  • CleanTalk WordPress Plugin Vulnerability Threatens Up To 200K Sites

    An advisory was issued for a critical vulnerability rated 9.8/10 in the CleanTalk Antispam WordPress plugin, installed in over 200,000 websites. The vulnerability enables unauthenticated attackers to install vulnerable plugins that can then be used to launch remote code execution attacks. CleanTalk Antispam Plugin The CleanTalk Antispam plugin is a subscription based software as a…

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *