Dakwah-Lens
BriefingDiskusiPustaka KitabGaleri Flyer
Dakwah-LensMembantu para da'i dengan insights media berbasis AI. Sebuah inisiatif nirlaba dari Sukses & Berkah Group.

Produk

BriefingCara KerjaPustaka KitabDonasi

Tautan

Tentang KamiKontakPrivasiKetentuan
© 2026 Sukses & Berkah GroupDakwah-Lens

Untuk engineer, mahasiswa, dan yang ingin tahu

Di balik layar: bagaimana Dakwah-Lens dibangun.

Stack AI yang tidak sederhana, dikemas dalam monorepo yang ringkas. Halaman ini menelusuri setiap lapisan — layanan, model, alur data, dan keputusan di baliknya.

Baca source di GitHubKembali ke beranda

Ringkasan

Arsitektur

Monorepo — frontend Next.js 16 + backend FastAPI/Celery berbagi satu Postgres. Qdrant menyimpan embedding kitab; Redis menjadi broker Celery.

Stack AI

Provider-agnostik. Gemini Pro untuk sintesis · Claude Sonnet 4.5 fallback · Gemini Flash-Lite untuk sentimen + relevansi · Gemini untuk penemuan topik.

Jaminan utama

Setiap rujukan Al-Qur'an / hadits diambil dari pustaka — tidak pernah dikarang oleh LLM.

Open-source dengan lisensi MIT.

Stack sekilas.

Lima lapisan — UI, aplikasi, data, AI, infrastruktur. Tiap kotak menyebut library + fungsinya supaya Anda bisa langsung jelajahi source-nya.

Frontend

  • Next.js 16App Router, server components, server actions. Render SSR untuk SEO dan halaman marketing.
  • React 19Concurrent rendering memungkinkan fallback loading.tsx level rute.
  • Tailwind v4Utility CSS, tanpa design-system framework — bundle tetap ramping.
  • next-intl v4i18n untuk `id` (default) + `en`. Semua copy ada di /messages/*.json.
  • Auth.js v5Email/password + Google OAuth. Sesi JWT dengan gate verifikasi email.
  • Drizzle ORMORM sisi TS. Mirror skema SQLAlchemy untuk read yang type-safe.
  • lucide-reactSatu set ikon, konsisten di seluruh aplikasi.

Backend (Python 3.12, dikelola uv)

  • FastAPIAPI async untuk FE + entry-point CLI scripts.
  • SQLAlchemy 2.0ORM async. Model ada di api/src/api/models/.
  • AlembicMigrasi skema — satu sumber kebenaran.
  • Celery + RedisPenjadwalan ingest, batch klasifikasi, klastering topik, metrik sistem.
  • uv (Astral)Deps + virtualenv super cepat via uv dari Astral. Menggantikan poetry/pip.
  • Pydantic v2Settings + model request/response; konfigurasi via env.
  • structlogLog terstruktur JSON.

Data

  • PostgreSQL 16OLTP utama — users, social_posts, briefs, semua tabel observabilitas.
  • QdrantVector DB. Menyimpan embedding korpus kitab (~6.236 ayat + hadits).
  • RedisBroker + backend hasil Celery.

AI / ML

  • OpenAI Embeddingstext-embedding-3-large untuk embedding pustaka kitab (Al-Qur'an, hadits, tafsir) dan mencocokkan query topik kajian lewat Qdrant.
  • Gemini 2.5 Pro / Flash-LitePro untuk sintesis kajian + briefing mingguan; Flash-Lite untuk sentimen + relevansi + topic discovery + reranking dalil + moderasi komentar.
  • Anthropic Claude Sonnet 4.5Fallback sintesis kajian saat Gemini tidak tersedia.
  • trafilaturaEkstraksi badan artikel untuk feed RSS yang ringkas summary-nya.

Infrastruktur & ops

  • Docker ComposeDev lokal: docker compose up menghidupkan Postgres + Qdrant + Redis.
  • IDCloudHost VPSDi-host di Jakarta sesuai UU PDP §17 untuk residensi data.
  • ResendEmail transaksional — verifikasi + reset password.
  • ApifyActor scraping untuk X / Instagram / TikTok.
  • YouTube Data API v3YouTube Data API langsung (lebih murah dari actor YouTube Apify).

Arsitektur sistem.

Empat lapisan, masing-masing punya satu tugas. Worker Celery klien kelas-satu data layer — bicara langsung ke Postgres seperti web app.

Klien

Browser

Next.js SSR + RSC

Worker Celery

Celery beat + worker

Skrip CLI

uv run python -m api.scripts.*

Aplikasi

Next.js Server Actions

Auth, kajian, admin

FastAPI

Health, API publik (future)

Data layer

PostgreSQL

OLTP

Qdrant

Vector search

Redis

Queue + broker

Layanan eksternal

LLM providers

OpenAI · Gemini · Anthropic

Scrapers

Apify · YouTube API · RSS

Resend

Email transaksional

Pipeline ingest.

Lima tahap. Tiap beat tick mem-fan-out N keyword sebagai child task independen — failure isolation per keyword, bukan hanya per platform.

  1. 01

    Scrape

    services/{apify,rss,youtube}.py

    Lima platform: X / Instagram / TikTok via Apify, YouTube via Data API, RSS mainstream via feedparser + trafilatura untuk ekstraksi body penuh. Semuanya kembali dalam bentuk `ScrapeResult` yang seragam.

  2. 02

    Normalisasi

    services/normalizers.py

    Normalizer per-platform memetakan raw payload → dict baris SocialPost. Ekstraksi handle author, pemotongan URL, parsing posted_at, tagging region untuk RSS.

  3. 03

    Klasifikasi

    services/sentiment.py · relevance.py

    Empat pass Gemini Flash-Lite paralel per postingan — (1) sentimen event-valence terpadu untuk semua platform (sejak IndoBERT dipensiunkan 2026-05-25), (2) relevansi da'wah 9-kategori, (3) skor da'wah-opportunity, (4) klasifikasi ke salah satu dari 14 kelompok tema dakwah (Hukum & Keadilan, Aqidah & Ibadah, Konflik & Geopolitik, Toleransi & Lintas-Iman, dst.) yang ditulis ke kolom `theme_group` di `social_posts` dan menjadi sumbu utama dashboard + briefing mingguan. Biaya gabungan: ~$0.0003 per postingan.

  4. 04

    Upsert

    scripts/ingest.py → social_posts

    Konflik di (platform, external_id) → update text + klasifikasi + region. Menjalankan ulang scrape tidak pernah menghasilkan duplikat.

  5. 05

    Penemuan topik

    scripts/cluster_topics.py → topics

    Penemuan topik via Gemini Flash-Lite tiap hari, per platform — dijalankan Celery beat pada 04:00 WIB. Membaca seluruh postingan dalam 7 hari terakhir di atas ambang da'wah-opportunity (biasanya 300–600 postingan media arus utama), lalu meminta Gemini mengidentifikasi 6–10 tema dengan label Bahasa Indonesia yang dibingkai dari sudut da'wah. Tiga gerbang post-LLM menjaga kualitas hasil: (a) **elastic-word filter** — label yang mengandung kata 'misterius/tragis/viral/polemik/kontroversi/heboh/geger' otomatis di-inject exclude-list, setelah audit 2026-06-02 menunjukkan tema 'Peristiwa Misterius & Horor' menyedot 70% post kecelakaan-lalu-lintas yang tidak relevan; (b) **cosine merge 0.78** — embedding 3072-dim untuk dedup near-duplicate antar-tema (diturunkan dari 0.85); (c) **orphan-rescue** — post 'Lainnya' yang punya `theme_group` sama dengan salah satu tema yang lolos dan cosine ≥0.20 ke centroid-nya akan dipindah ke tema itu, supaya 'Lainnya' tidak menelan post yang sebetulnya tematik. Hasil ditulis ke `topics`.

Dijadwalkan via Celery beat (Asia/Jakarta): RSS arus utama tiap 2 jam · X mingguan tiap Rabu pukul 22.00 WIB (cap 500 item/keyword, jendela 7 hari) · TikTok mingguan Rabu 22.10 WIB dan Instagram mingguan Rabu 22.20 WIB (limit 20 item/keyword, fase evaluasi sejak 2026-05-25) · sapu channel YouTube whitelist mingguan tiap Rabu pukul 21.00 WIB (Data API, jalur uploads — suara da'i tepercaya) · overlay tren harian pukul 12.00 WIB: keyword da'wah-relevan di-scrape ke X (apidojo) + pencarian YouTube tak-terikat-channel (Data API search.list, dengan gerbang bahasa) · penemuan topik Gemini pukul 04.00 WIB tiap hari (mainstream + YouTube) · penemuan topik X + TikTok + Instagram tiap Kamis pukul 04.00 WIB · briefing insights tiap Kamis pukul 05.00 WIB — sehari sebelum khutbah Jumat (saat ini di-pause cost-mode, di-trigger manual via Claude) · digest email tiap Kamis pukul 08.00 WIB · rekonsiliasi tagihan Apify pukul 06.00 WIB tiap hari. Run manual via `uv run python -m api.scripts.ingest`.

Penemuan keyword: rotasi kurasi + overlay tren.

Dua lapisan saling melengkapi. Sapu kurasi mingguan menjamin cakupan struktural di kelompok tema dakwah; overlay tren harian menangkap peristiwa hari ini yang tidak akan pernah masuk daftar kurasi.

Rotasi kurasi

services/ingest_queries.py · workers/ingest.py::rotating_ingest

~49 keyword tersebar di kelompok tema dakwah utama (religi, keluarga, remaja, muamalah, keadilan sosial, pendidikan, kesehatan, peristiwa terkini, kultural), bisa diedit superadmin di /admin/system/queries. AKTIF SAAT INI: RSS arus utama (tiap 2 jam via feedparser, tanpa keyword) dan X (apidojo, $0.0004/item, mingguan tiap Rabu 22.00 WIB, cap 500 item/keyword + jendela 7 hari). FASE EVALUASI 1 MINGGU sejak 2026-05-25: TikTok (clockworks/free-tiktok-scraper, $0.004/item, mingguan Rabu 22.10 WIB, limit 20 item/keyword) dan Instagram (apify/instagram-hashtag-scraper, $0.0023/item, mingguan Rabu 22.20 WIB, limit 20 item/keyword) — biaya dan akurasi sentimen akan dinilai setelah run pertama. YOUTUBE (sejak 2026-05-27): sapu channel whitelist terverifikasi mingguan tiap Rabu 21.00 WIB (Data API gratis, jalur uploads — separuh "suara da'i tepercaya" dari split YouTube); butuh seed channel + verifikasi admin satu kali. Code path semua siap.

Overlay tren

services/trending_topics.py · workers/ingest.py::trending_ingest

AKTIF sejak 2026-05-25 — diaktifkan kembali bersamaan dengan scraper X mingguan. Setiap hari pukul 12.00 WIB, tiga sumber resmi gratis digabungkan: Google Trends Indonesia RSS, YouTube Data API mostPopular regionCode=ID, Google News Indonesia RSS. Satu panggilan Gemini Flash-Lite mengekstrak keyword pendek dari tiap kandidat sekaligus menilai relevansi situasi sosial (KEEP jika menyentuh masyarakat/kebijakan/keluarga/remaja/ekonomi/dll., SKIP hanya untuk obrolan hiburan murni). Keyword yang lolos (sampai 8) di-scrape ad-hoc di X (apidojo) dan dicari di YouTube tak-terikat-channel (Data API search.list, dengan gerbang bahasa untuk menahan spam lintas-bahasa) — separuh "apa pun yang sedang viral" dari split YouTube. Biaya marjinal ~$1/bulan (YouTube gratis dalam kuota).

Kenapa dua lapis: kurasi saja menyisakan jeda refresh 7–25 hari per keyword — cukup untuk topik evergreen (riba, ibadah) tapi buta terhadap insiden viral yang baru meledak pagi itu. Tren saja kelebihan muatan hiburan (fandom K-pop, olahraga). Hybrid mencakup keduanya.

Cara scraping per platform

Strategi pengumpulan berbeda di tiap platform — menyesuaikan biaya, batas rate-limit, dan kualitas sinyal. Berikut metode persisnya.

Media arus utama (berita)

TANPA keyword. Kami membaca SELURUH feed RSS dari ~27 media Indonesia (Kompas, Detik, Antara, Republika, Tempo, CNN, dll.) tiap 2 jam, lalu mengekstrak teks penuh artikel via feedparser + trafilatura. Gratis, tanpa Apify — sumber utama dan paling padat.

X (Twitter)

Berbasis KEYWORD. Pool ~49 keyword da'wah-relevan (editable superadmin) di-scrape mingguan tiap Rabu 22.00 WIB via Apify (apidojo), dengan cap 500 item/keyword dan jendela 7 hari. Ditambah keyword tren da'wah-relevan dari overlay harian pukul 12.00 WIB.

TikTok

Berbasis KEYWORD. Pool keyword yang sama di-scrape mingguan Rabu 22.10 WIB via Apify (clockworks/free-tiktok-scraper), limit 20 item/keyword. Dalam fase evaluasi sejak 2026-05-25.

Instagram

Berbasis HASHTAG. Keyword pool dipetakan ke hashtag, di-scrape mingguan Rabu 22.20 WIB via Apify (instagram-hashtag-scraper), limit 20 item/keyword. Dalam fase evaluasi.

YouTube — dua strategi

(1) MINGGUAN, berbasis CHANNEL: daftar channel da'i terverifikasi (whitelist) diambil uploads terbarunya tiap Rabu 21.00 WIB via YouTube Data API (jalur uploads, sangat murah) — ini lapisan "suara da'i tepercaya". (2) HARIAN, berbasis PENCARIAN: keyword tren da'wah-relevan dicari ke SELURUH YouTube (search.list, tak terikat channel) pada overlay 12.00 WIB, dengan gerbang bahasa untuk menahan spam lintas-bahasa — ini lapisan "apa pun yang sedang viral". Keduanya gratis dalam kuota Data API.

Mainstream RSS adalah tulang punggung (gratis, padat); platform sosial berbiaya Apify dijalankan mingguan dengan keyword/hashtag untuk menjaga biaya; YouTube memakai Data API gratis dengan dua jalur sekaligus.

Kenapa jumlah post dari Instagram & TikTok jauh lebih sedikit dibanding platform lain?

Alasan utamanya biaya. Mainstream RSS gratis (kami tarik tiap 2 jam), YouTube Data API juga gratis, dan X relatif murah (~$0.0004 per post). Tapi Instagram (~$0.0023 per post via apify/instagram-hashtag-scraper) dan TikTok (~$0.004 per post via clockworks/free-tiktok-scraper) menagih per item, dan budget Apify kami $29/bulan. Karena itu kami batasi limit per keyword (IG 60, TT 20) dan jalankan mingguan tiap Rabu malam, bukan harian seperti mainstream RSS.

Lalu kenapa tetap discrape meskipun jumlahnya kecil? Karena IG dan TikTok memberi gambaran percakapan dari audiens yang sama sekali berbeda — terutama Gen Z. Pola bahasa, isu yang viral, format konten, dan reaksi emosional di TikTok sangat berbeda dari mainstream news atau X (yang lebih dewasa/news-reader). Bahkan sampel kecil ~600–1.500 post per minggu cukup untuk menangkap signal: kategori dakwah mana yang sedang ramai di Gen Z, miskonsepsi yang muncul, dan format konten yang efektif untuk audiens muda. Tanpa scrape IG/TT, briefing dakwah jadi bias ke audiens news-reader (mayoritas 30+ tahun).

Briefing insights mingguan.

Apa yang sistem (atau operator) tulis tiap Kamis untuk semua orang — hingga 14 briefing per cycle (satu per kelompok tema yang mencapai ambang volume) dalam Bahasa Indonesia.

  1. 01

    Trigger

    workers/celery_app.py + scripts/manual_briefing.py

    Cron Celery Kamis 05.00 WIB sudah disiapkan (1 jam setelah recluster topik 04.00, sehari sebelum khutbah Jumat) — tapi DIPAUSE per 2026-05-23 untuk hemat biaya selama fase pengembangan. Saat ini operator menjalankan manual lewat `manual_briefing.py dump <kelompok-slug>` yang menjalankan pipeline data-prep lalu menampilkan prompt — jawaban Claude di-save kembali via `manual_briefing.py save`. Re-enable cron dengan uncomment block di celery_app.py.

  2. 02

    Hitung headline stats

    services/insights_summary.py::_compute_stats

    Agregasi 7 hari dari `social_posts`: komposisi sentimen, top kelompok tema dengan share-pct, top topik dengan sample headlines per topik. Saat belum ada baseline minggu lalu, delta_pp diisi null — tidak memfabrikasi perubahan. Briefing per-kelompok memfilter berdasarkan kolom `theme_group` di tabel social_posts (atau fallback regex pada topic.label).

  3. 03

    Tarik dalil

    services/kitab_retrieval.py + rerank_daleel (top_n=10)

    Flash-Lite menyusun query retrieval dari stats. OpenAI meng-embed query. Qdrant cari di semua 6 koleksi kitab (~4 hit per koleksi → 15 kandidat). Flash-Lite pass kedua me-rerank pool jadi 10 dalil paling tematik. Hadis di-translate ke ID bila belum ada — Quran sudah punya ID + EN (Kemenag + Sahih International). Pool 10 dalil (sebelumnya 5) supaya tiap sub-section di Section 4 bisa weave 2-3 inline citation tanpa mengulang.

  4. 04

    Sintesis (Bahasa Indonesia)

    Gemini 2.5 Pro (paused) → Claude manual

    Mode otomatis (cron aktif): Gemini 2.5 Pro panggil sekali dengan max_output 32.768 token + thinking budget 12.288 token. Mode manual saat ini: paste prompt + konteks ke Claude (chat), save reply lewat manual_briefing.py. Output identik di kedua mode: markdown 5-bagian 7.300-9.800 kata dalam Bahasa Indonesia. Dalil-grounded — model HANYA boleh mengutip dari 10-dalil pool yang di-retrieve. Versi Bahasa Inggris dijeda per 2026-05-23 (lokal banner muncul untuk pembaca EN).

  5. 05

    Persist + render

    briefings → /briefings/[id] + Puppeteer flyers

    Tulis baris ke `insights_summaries` (summary_md, headline_stats, daleel_refs, segment, model, tokens, cost). Halaman publik /briefings/brief/{slug} render dengan TOC sticky + share/download toolbar. Section 4 di-split jadi 6 kartu deliverable interaktif (klik buka modal). Flyer dirender on-demand oleh Puppeteer + HTML/Tailwind — 2 varian (general + Gen-Z) per briefing plus 6 varian per-deliverable. Cache CDN 1 jam, immutable.

Anatomi satu briefing

Apa yang masuk ke kotak konten setiap pekan. Satu briefing = 5 bagian markdown (Bagian 4 berisi 8 deliverable terpisah) + 1 poster kampus dengan QR + 6 flyer share-ready (termasuk ajakan sunnah pekan ini + 1 doa yang bisa diwirid).

Struktur markdown

  • 1~100-130 kata

    Ringkasan eksekutif

    Satu paragraf pembuka: 3 kategori teratas dengan share-pct, komposisi sentimen, dan tema utama pekan ini.

  • 2~250-350 kata

    Numerik & tren

    Detail headline stats: per-kategori volume + delta, sample headlines per topik, perubahan sentimen minggu-ke-minggu.

  • 3~350-450 kata

    Tema utama & pola

    Narasi tematik yang mengikat headline mentah menjadi pola yang relevan untuk dakwah pekan ini.

  • 5~500-700 kata

    Dalil & sumber

    Bibliography 8-10 dari pool 10 yang di-retrieve. Format: citation bold + terjemahan + konteks 1-2 kalimat. Diakhiri disclaimer AI-assisted.

Bagian 4 — Content Kit

Strategi & Aksi Dakwah — 8 deliverable siap-pakai dalam satu briefing

5,400-7,800 kata
  • Khutbah Jumat lengkap

    ~2,300-3,200

    Dari mukadimah, ayat pembuka, inti 6-9 paragraf, doa penutup transliterasi Arab — tinggal disesuaikan dengan jamaah.

  • Outline kajian ibu-ibu

    ~800-1,100

    Format 45 menit: 3 talking-point + contoh berita pekan ini + Q&A antisipatif + penutup.

  • Conversation script rumah

    ~500-700

    3-4 skenario obrolan orang tua dengan anak (SD/SMP/SMA): pertanyaan pembuka + respons antisipatif + penutup.

  • Script video kreator

    ~100-130

    60-90 detik untuk TikTok/IG Reels/Shorts: hook 5 detik → body 40-60 detik dengan 1 dalil → CTA.

  • Paket Mahasiswa untuk papan kampus

    ~800-1,100

    Poster (pertanyaan provokatif + QR) → halaman artikel magazine-style + Q&A realistis. Framing berbasis logika untuk mahasiswa yang menolak ceramah top-down.

  • Aksi sosial per kelompok usia

    ~600-900

    4 aksi konkret kecil-budget (< Rp 500.000) untuk anak / remaja / dewasa / lansia — bisa jalan paralel sebagai 1 kampanye RT.

Bonus visual

6 flyer share-ready + 1 poster kampus, dirender on-demand

Puppeteer + HTML/Tailwind merender flyer 1080×1080 PNG dari konten briefing yang sudah ada. composeFlyer() pilih layout + image dari registry secara deterministik per edition+slot, jadi share-URL stabil tapi variasi visual berputar tiap pekan.

  • Flyer Umum — klasik Islamic

    Layout ayat-centered dengan kaligrafi Amiri, ornamen bintang-8, foto masjid / interior sebagai backdrop. Cocok untuk grup masjid, pengajian dewasa, story personal.

  • Flyer Gen Z — bolder & headline-led

    Headline punchy 64-78px sebagai focal element, palette violet→fuchsia→amber, ayat sebagai supporting card miring. Dirancang khusus untuk feed IG / TikTok.

Plus 6 flyer per-deliverable (khutbah / kajian / home / content / genz / action) yang muncul di modal kartu Section 4. Total 8 flyer × hingga 14 briefing = sampai 112 PNG share-ready per cycle, semuanya pre-rendered Puppeteer untuk percetakan IG/print-quality.

Disiplin syar'i yang sama dengan kajian pengguna: prompt long-form secara eksplisit melarang kutipan Quran/hadits freelance. Setiap referensi datang dari pool 10 dalil hasil rerank — kalau model mencoba menambah, itu pelanggaran aturan prompt. Disclaimer dimasukkan di Bagian 5 setiap briefing: AI-assisted, BUKAN fatwa otoritatif.

Cara kajian dihasilkan.

Jaminan keras: setiap referensi Al-Qur'an + hadits diambil dari korpus dengan pencarian semantik — tidak pernah dikarang LLM. LLM hanya menyusun konteks di sekitarnya.

  1. 01

    Capture

    app/[locale]/briefs/new

    User submit topik + kelompok tema + nada + locale + konteks tambahan opsional. Profil onboarding dilampirkan di sisi server.

  2. 02

    Embed topik

    lib/kitab-retrieval.ts

    OpenAI text-embedding-3-large mengubah topik menjadi vektor 3072-dim. Diperkaya dengan label kelompok tema supaya retrieval condong ke ayat, hadits, dan tafsir yang relevan dengan tema.

  3. 03

    Tarik dalil

    Qdrant: all kitab collections

    Pencarian cosine paralel pada semua koleksi kitab di Qdrant (~4 kandidat per kitab, ~15 total). Pass kedua via Gemini Flash-Lite me-rerank pool tersebut secara tematik menjadi 5 dalil yang paling relevan — cosine saja sering memunculkan ayat yang punya token sama dengan query tapi meleset dari sudut da'wah-nya. Kalau retrieval gagal, generator kajian akan memunculkan pesan error ke UI.

  4. 04

    Sintesis

    lib/llm.ts → generateJson()

    Gemini Pro (primary) menerima topik, profil, konteks tambahan, dan dalil yang ditarik. Fallback ke Claude Sonnet 4.5 kalau Gemini gagal. Output dibatasi ke schema JSON.

  5. 05

    Validasi & simpan

    Zod validation → briefs table

    Zod memvalidasi bentuk respons, melekatkan array dalil yang diambil, lalu menyimpan ke `briefs`. Kegagalan muncul sebagai error bertipe ke form — tidak pernah berakhir menjadi baris setengah jadi.

Aturan keras per PRD §12: system prompt secara eksplisit melarang LLM mengarang ayat Al-Qur'an atau kutipan hadits tambahan. Model hanya melihat array dalil yang kami ambil — kalau mencoba menambah, validasi akan menangkapnya.

Model yang dipakai, dan kenapa.

Setiap pilihan adalah model terkecil yang menyelesaikan tugasnya dengan baik, bukan yang terbesar yang tersedia.

ModelPeranKenapa yang iniDi mana berjalan

Gemini 2.5 Flash-Lite

gemini-2.5-flash-lite

Single classifier untuk semua platform sejak 2026-05-25 (gantikan IndoBERT yang dipensiunkan): (a) sentimen event-valence, (b) relevansi da'wah 9-kategori (aqidah, akhlaq, muamalah, social_justice, family, youth, education, economic_ethics, health), (c) skor opportunity, (d) **klasifikasi 14 kelompok tema dakwah** ke kolom `theme_group` (Hukum & Keadilan / Aqidah & Ibadah / Konflik & Geopolitik / Toleransi & Lintas-Iman / Sosial & Keluarga / Ekonomi & Bisnis / Pendidikan & SDM / Kesehatan & Kehidupan / Lingkungan & Bencana / Pemerintahan & Kebijakan / Patologi Sosial Digital / Teknologi & AI / Pekerja & Pertanian Rakyat / Inspirasi & Kisah Pribadi), (e) penemuan topik harian dengan filter elastic-word + cosine-merge 0.78 + orphan-rescue, (f) reranking dalil, (g) moderasi komentar diskusi.Tier Gemini termurah. Biaya per postingan ~$0.0003 untuk seluruh classifier — cocok dengan cap biaya operasional IDR 1.5-2jt/bulan dengan headroom yang nyaman.Dipanggil dari api/src/api/services/relevance.py (batch 10), api/src/api/services/topic_discovery.py, dan api/src/api/services/kitab_retrieval.py (rerank).

Gemini 2.5 Pro

gemini-2.5-pro

Briefing mingguan (Kamis 05.00 WIB) — hingga 14 kelompok tema dalam Bahasa Indonesia, ~7.300-9.800 kata markdown long-form per briefing, dalil grounded dari retrieval. Cron-nya satu — `generate_briefings`.Reasoning kelas atas dengan biaya ~35% lebih rendah dari Claude Sonnet 4.5. Briefing mingguan ~$0.06 per briefing-bahasa (~$3,40/bulan untuk 14 kelompok × 1 bahasa); kajian on-demand di-route lewat /briefs/new dan dibayar per-pemicuan.Jalur insights: api/src/api/services/briefings_summary.py. Jalur kajian on-demand: web/src/lib/llm.ts → callGemini().

Claude Sonnet 4.5

claude-sonnet-4-5

Fallback sintesis kajian saat Gemini error.Cloud berbeda, tapi kualitas mirip. Mengurangi risiko outage single-vendor.Jalur fallback di web/src/lib/llm.ts → callAnthropic().

OpenAI text-embedding-3-large

text-embedding-3-large

Teks topik → vektor untuk retrieval semantik Al-Qur'an.Embedding 3072-dim memberikan retrieval yang terasa lebih akurat dibanding -3-small pada drift terjemahan Arab klasik.Dipanggil dari web/src/lib/quran-retrieval.ts sebelum tiap query Qdrant.

Estimasi biaya operasional bulanan

Angka nyata, terkini — platform sengaja dijalankan dengan anggaran ketat. Total biaya operasional (server + API) dicap pada IDR 1.5-2 juta (~$92-123) per bulan. Jadwal & tarif per Mei 2026.

ProviderUntuk apaBulananCatatan

Gemini 2.5 Pro (user briefs only)

Model utama sintesis briefing eksekutif mingguan — dipanggil manual via scripts/manual_briefing.py (auto cron dipause 2026-05-23).$0.30On-demand user briefs only · ~$0.07/brief · auto-cron paused

Gemini Flash-Lite

Relevansi + news-sentimen + fallback non-ID + penemuan topik + filter tren.$17.40Per-post classifier + daily topic-discovery + daily trending fan-out + weekly social burst

OpenAI embeddings

Embedding korpus kitab (Qur'an + 4 kitab hadits).$0.20Topic-recluster vectors + per-brief kitab query embeddings

YouTube Data API

Sapu channel whitelist mingguan + pencarian tren harian via Data API v3 (kuota gratis 10K unit/hari).—Di dalam kuota gratis harian

RSS (feedparser + trafilatura)

Media arus utama tiap 2 jam, tanpa biaya API.—Gratis

Anthropic Claude (fallback)

Fallback sintesis brief saat Gemini Pro error.—Fallback on-demand, skala dengan pemakaian

VPS · IDCloudHost (Jakarta)

VPS Indonesia: hosting aplikasi, Postgres, Redis, Qdrant Cloud (free tier).$18.00≈ Rp 300K/bulan · 2 vCPU · 4 GB RAM · 30 GB disk

Apify · X (apidojo, incl. late-bill)

Scrape tweet X via apidojo/tweet-scraper, mingguan Rabu 22.00 WIB (50 keyword aktif, cap 500 item/keyword, jendela 7 hari).$20.21Weekly Wed 22:00 + daily trending overlay · ~$0.016/tracked-call · apidojo bills late, captured by daily reconcile

Apify · Instagram

Scrape postingan hashtag Instagram, mingguan Rabu 22.20 WIB (37 hashtag aktif, limit 60 item/keyword sejak 2026-05-30).$5.9737 kw · weekly Wed 22:20 · ~$0.046/item · real volume ~40% of projection

Apify · TikTok

Scrape video TikTok, mingguan Rabu 22.10 WIB (28 keyword aktif setelah prune zero-yield 2026-05-30, limit 20 item/keyword).$12.2828 kw (post-prune) · weekly Wed 22:10 · ~$0.063/call (highest per-call in stack)
Total estimasi~$74.36≈ Rp 1.212K/bulan

Batas anggaran (PRD §13)

$74.36 / $123 · Rp 2.005K

Snapshot per 2026-06-07. Sumber otoritatif Apify: $24,80 terpakai dari cap Starter $29 di hari ke-20 dari 31 (cycle 19 Mei → 18 Juni) — burn rate $1,24/hari diproyeksikan tutup cycle di ~$38, ~$9 di atas plan cap. TikTok per-call $0,063 (tertinggi di stack) jadi tuas pengurangan terbesar berikutnya. Job rekonsiliasi tagihan harian pukul 06.00 menangkap selisih late-billed events di X. Sintesis brief (Claude / Gemini Pro) on-demand — skala dengan volume pengguna pasca-launch.

Korpus kitab — 19 kitab yang di-embed di Qdrant.

Korpus v0 sesuai PRD §12 — 19 kitab dalam 5 kategori (Qur'an & tafsir, hadits, sirah & sahabat, fiqh, aqidah & adab). Di-embed sekali, tidak pernah ditulis ulang oleh aplikasi — re-embed admin-only via skrip embed.

Qur'an & Tafsir

  • Al-Qur'an al-Karim

    Seluruh 6.236 ayat dalam Arab + Indonesia (Kemenag) + Inggris (Sahih International). Ditag keyword topik oleh Gemini untuk recall retrieval yang lebih baik.

  • Tafsir Ibn Katsir

    Hafiz Ibn Katsir, edisi ringkasan Mubarakpuri (AR + EN). Tafsir per ayat untuk seluruh 6.236 ayat — dipotong ~1000 token agar retrieval menemukan paragraf, bukan entri penuh.

Hadits utama

  • Sahih al-Bukhari

    Imam al-Bukhari · 7.275 hadits dalam 97 kitab. Struktur bab lengkap dengan nomor hadits.

  • Sahih Muslim

    Imam Muslim · ~7.500 hadits dalam 56 kitab. Embedding bilingual EN+ID — 7.360 terjemahan Indonesia kurasi manual.

  • Riyad as-Salihin

    Imam an-Nawawi · 1.896 hadits dalam 372 bab. Koleksi hadits pedagogis, tertata per topik akhlak.

  • Bulugh al-Maram

    Ibn Hajar al-'Asqalani · 1.567 hadits hukum tersusun per topik fiqh. Rujukan utama fiqh berbasis daleel.

Sirah & Sahabat

  • Sirah Ibnu Hisyam

    Abdul Malik bin Hisyam (w. 218H) · tahqiq Sirah Ibn Ishaq dalam 2 jilid: nasab Nabi, kelahiran, dakwah Mekah, hijrah, ghazwah, fathu Makkah, haji wada'.

  • Asy-Syama'il al-Muhammadiyyah

    Imam at-Tirmidzi (w. 279H) · ~415 hadits dalam 55 bab tentang sifat fisik, akhlak, dan keseharian Nabi ﷺ. Materi sirah/akhlak fundamental pesantren.

  • Hayatus Shahabah

    Syaikh M. Yusuf al-Kandahlawi · kumpulan kisah & biografi Sahabat tersusun tematik (dakwah, iman, jihad, ibadah, akhlak). Referensi luas di halaqah modern.

  • Al-Bidayah wan-Nihayah

    Ibn Katsir · sejarah Islam mulai penciptaan, kisah para nabi, Sirah Nabawiyah, hingga khilafah Umayyah & Abbasiyah. 2.529 fasal, edisi Hijr.

Fiqh

  • Al-Umm

    Imam asy-Syafi'i · kitab induk fiqh Syafi'i, 44 kitab (Thaharah s/d Nafaqat) dalam 1.332 bab. Sumber primer madzhab Syafi'i.

  • Fiqh as-Sunnah

    Sayyid Sabiq · fiqh kontemporer berbasis daleel, tidak terikat satu madzhab. 866 bab dalam 62 kitab fiqh.

  • Fath al-Mu'in

    Syaikh Zainuddin al-Malibari (w. 987H) · matan fiqh Syafi'i klasik, kurikulum pesantren NU/tradisional. 13 bab utama.

  • Fath al-Qarib al-Mujib

    Ibnu Qasim al-Ghazzi (w. 918H), syarah matan At-Taqrib karya Abu Syuja' · pintu masuk fiqh Syafi'i di pesantren. 212 fasal, 17 kitab.

Aqidah & Adab

  • Bidayatul Hidayah

    Imam al-Ghazali · 28 fasal adab ibadah, menjauhi maksiat, dan adab pergaulan. Kitab akhlak klasik, edisi Maktabat Madbouli (1413H).

  • Nashaihul Ibad

    Syaikh Nawawi al-Bantani · syarah Munabbihat Ibn Hajar. Nasihat akhlak & tasawuf tersusun per bilangan (Tsunai s/d 'Asyari). Klasik pesantren.

  • Adab al-'Alim wa al-Muta'allim

    Badruddin Ibnu Jama'ah al-Kinani (w. 733H) · sumber rujukan KH Hasyim Asy'ari untuk kurikulum adab NU. 5 bab: keutamaan ilmu, adab guru, murid, kitab, dan madrasah.

  • 'Aqidat al-'Awam

    Syaikh Ahmad al-Marzuqi al-Maliki (w. 1262H) · matan aqidah Asy'ariyah dalam nazhom/syair. Materi pesantren tingkat dasar (TK-ibtidaiyyah).

  • Thalathat al-Ushul

    Syaikh Muhammad bin Abdul Wahhab (w. 1206H) · matan ringkas tauhid Salafi: tiga prinsip wajib bagi setiap muslim. Pendamping 'Aqidat al-'Awam untuk perspektif manhaj berbeda.

Setiap entri di Qdrant membawa (a) teks Arab aslinya, (b) terjemahan di locale kajian, dan (c) sitasi kanonik (kitab + bab + nomor ayat/hadits). Kajian selalu menautkan kembali ke sumber yang disebut sehingga da'i bisa memverifikasinya sebelum disampaikan.

Jelajahi seluruh 19 kitab di /kitab

Observabilitas — bagian yang membosankan tapi penting.

Sebagian besar tabel ini adalah log event append-only. Mereka menyokong dashboard superadmin /admin/system.

social_posts

Setiap item hasil scrape, lengkap dengan output classifier. Diindeks di (platform, posted_at), (platform, region), dan unik (platform, external_id) untuk dedup.

topics

Klaster tema yang ditemukan oleh topic discovery Gemini per platform — di-refresh otomatis tiap malam.

briefs

Kajian yang dihasilkan pengguna. Menyimpan dalil yang ditarik secara inline (JSONB) supaya pembacaan kajian tidak perlu hit ulang Qdrant.

usage_events

Setiap panggilan API berbayar (OpenAI, Gemini, Anthropic, Apify, YouTube, RSS, Resend) dengan biaya + token. Sumber data dashboard API costs.

system_metrics

Snapshot psutil tiap 60 detik. Sumber data chart CPU / memori / disk host.

ingest_runs

Siklus start_run / finish_run untuk tiap task Celery. Sumber data dashboard pipeline health.

page_views

Cookie sesi anonim httpOnly. Tanpa IP, tanpa fingerprint. Sumber data web analytics.

donations · manual_costs

Entri invoice VPS / domain manual + donasi yang tercatat. Sumber data halaman publik /transparency.

contact_messages

Submission form /contact publik. Diforward ke ADMIN_EMAIL dan disimpan sebagai cadangan durabel.

users · accounts · verification_tokens

Tabel standar NextAuth.js. Token verifikasi dipakai untuk alur verifikasi email + reset password.

Keputusan yang kami buat, dan apa yang kami tolak.

Teknologi penuh dengan judgement call. Ini versi eksplisit kami.

Kenapa monorepo dengan Python + TypeScript, bukan satu bahasa saja?
Python menang untuk sisi data/ML (integrasi Gemini/OpenAI/Anthropic, Qdrant client, embedding pipeline — semua paling matang di Python). Next.js + Auth.js + Drizzle menang untuk app sisi user. Memaksa ML di TS atau Auth.js-setara di Python berarti melawan ekosistem di tiap langkah. Biayanya: dua package manager, dikurangi karena uv + npm sama-sama cepat.
Kenapa Qdrant, bukan pgvector?
Kami sudah pakai Postgres untuk semua hal lain, jadi pgvector menggoda. Qdrant menang karena ergonomi filtering (named payload, hybrid search), dan karena saat ini ia jadi pilihan dominan di stack RAG produksi — lebih mudah cari bantuan debug dan tooling.
Kenapa IndoBERT dipensiunkan dan semua sentimen pindah ke Gemini Flash-Lite?
Awalnya hybrid: Gemini event-valence untuk berita arus utama, IndoBERT untuk post sosial (X/IG/TikTok/YouTube). Tapi eval manual 2026-05-25 pada 37 tweet 'korupsi' menunjukkan IndoBERT mislabel 6 dari 7 positif (sarkasme dibaca positif, opini suportif dibaca negatif, kritik retorik dibaca netral) — akurasi kelas positif ~14%. Setelah switch ke Gemini Flash-Lite untuk SEMUA platform, akurasi positif X naik ke ~92% pada sampel yang sama. Biaya tambahan: ~$1/bulan untuk sentimen X, tidak material di anggaran. Trade-off yang dibayar: torch + transformers + cache HuggingFace (~1.5 GB image worker) hilang dari dependency tree, plus latensi cold-start worker turun ~10-15 detik.
Kenapa Gemini Pro primary dan Claude Sonnet fallback, bukan sebaliknya?
Gemini 2.5 Pro ~35% lebih murah dari Sonnet 4.5 pada kelas kualitas yang setara untuk tugas long-form structured-output seperti ini. Menempatkannya di depan menurunkan biaya operasional secara signifikan; Sonnet jadi cadangan saat Google ada outage.
Kenapa tidak pakai framework seperti LangChain atau LlamaIndex?
Alur retrieval + sintesis kami ~80 baris TypeScript. Menambah framework artinya menambah 200 transitive dependency untuk nilai yang tidak kami pakai. Kami tidak chaining banyak model atau mendukung banyak vector DB — saat kebutuhan itu muncul, pertimbangannya berbalik.
Kenapa scraper Facebook belum masuk jadwal?
Fungsi `scrape_facebook()` ada di `apify.py`, tapi tidak ada entry beat yang memanggilnya. Tiga alasan. (1) Facebook menurun sebagai surface konten untuk muslim Indonesia muda — sebagian besar percakapan da'wah aktif sudah pindah ke TikTok dan X. (2) Actor Facebook di Apify termasuk yang paling mahal per item, dan yield-per-rupiah tidak sepadan. (3) Instagram sudah mewakili ekosistem Meta di jadwal aktif kami. Kode scraper-nya tetap siap, jadi hari data bilang kami kehilangan sesuatu, satu baris entry beat sudah cukup untuk mengaktifkannya.

Baca source-nya. Kirim PR.

Seluruh repo ada di GitHub di bawah lisensi MIT. Issue, PR, doa, dan masukan semua disambut — tidak ada langkah CLA dan tidak ada review board formal, hanya tim kecil yang akan membaca apa yang Anda kirim.

Buka repository

github.com/taufik-adinugraha/dakwah-lens