Een lokale Python-applicatie die inkomende berichten verzamelt, links opslaat en de basis klaarzet voor AI-verrijking en wekelijkse e-mailrapportage. Deze eerste versie focust op configuratie, database en een uitbreidbare projectstructuur.
- Python 3.11+ (aanbevolen)
- Pip
- macOS of Linux (ontwikkeld op macOS)
- Projectmap openen
cd project_root - Virtuele omgeving maken
python3 -m venv .venv
- Virtuele omgeving activeren
source .venv/bin/activate - Dependencies installeren
pip install -r requirements.txt
- Kopieer
.env.examplenaar.env.cp .env.example .env
- Vul de waarden in voor database, Telegram, OpenAI en SMTP.
- Laat
DATABASE_URLopsqlite:///./knowledge.dbvoor lokale opslag of pas aan naar een andere database. - Nieuwe variabelen voor de web UI:
DEFAULT_USER_ID: standaard user voor handmatige runs (default1).DASHBOARD_USERNAME/DASHBOARD_PASSWORD: Basic Auth credentials voor het dashboard (defaultadmin/admin– wijzig dit!).INTERNAL_TRIGGER_TOKEN: shared secret om de interne endpoints (/internal/run/*) te triggeren vanuit GitHub Actions of andere schedulers.EMAIL_VERIFICATION_CODE_TTL_MINUTES: verloopduur van verificatiecodes voor Telegram onboarding (default15).
Voer het init-script uit nadat de virtuele omgeving actief is en de .env klaar staat:
python scripts/init_db.pyDit maakt knowledge.db in de projectroot aan en creëert alle tabellen.
Werk je met een bestaande database? Draai dan ook:
python scripts/migrate_user_onboarding.py- Vraag bij @BotFather een bot aan en noteer de token.
- Zet
TELEGRAM_BOT_TOKENin je.env. - Zorg dat de database is geïnitialiseerd (zie vorige stap).
- Start de bot vanuit de projectroot:
python scripts/run_bot.py
- Stuur
/startnaar je bot om onboarding te starten (e-mail invoeren + code verifiëren).- Bij een foute of verlopen code kun je altijd
/resendsturen. - Met
/exportmailt de bot een JSON-export van al je eigen historische data (berichten, resources, reports, optioneel knowledge items).
- Bij een foute of verlopen code kun je altijd
- Na onboarding worden tekstberichten opgeslagen in
raw_messagesmetsource='telegram'. Links worden gedetecteerd via regex en gemarkeerd incontains_link.
- Elke unieke Telegram-gebruiker wordt automatisch aangemaakt in
usersop basis vantelegram_user_id. - Onboarding-statusflow:
new→awaiting_email→awaiting_email_verification→active. - Telefoonnummer is optioneel; delen gaat via Telegram contact share (
request_contact=True). - Berichten vóór verificatie worden al opgeslagen, maar digestmails gaan alleen naar users met
email_verified=true. - Bij upgrades op bestaande databases: run eenmalig
python scripts/migrate_user_onboarding.py(additieve migratie, geen drop/reset).
- Initialiseer opnieuw:
rm -f knowledge.db python scripts/init_db.py
- Start de bot, stuur een bericht vanaf Telegram en stop de bot weer:
python scripts/run_bot.py
- Controleer de database met
sqlite3 knowledge.dben voer onderstaande statements exact uit:.schema users; .schema raw_messages; SELECT id, telegram_user_id, telegram_chat_id, telegram_username, display_name FROM users ORDER BY id DESC LIMIT 10; SELECT id, user_id, text, contains_link FROM raw_messages ORDER BY id DESC LIMIT 10; SELECT rm.id AS message_id, rm.text, rm.user_id, u.telegram_user_id, u.telegram_username, u.display_name FROM raw_messages rm JOIN users u ON rm.user_id = u.id ORDER BY rm.id DESC LIMIT 10;
Nadat er berichten met links zijn opgeslagen kun je de backlog verwerken en resources vullen.
- Zorg dat
pip install -r requirements.txtis uitgevoerd zodathttpx,readability-lxml,beautifulsoup4enlxmlbeschikbaar zijn.- Let op:
lxml-html-cleanis nu vereist;pip install -r requirements.txtinstalleert dit automatisch.
- Let op:
- Voer het script uit:
python scripts/process_backlog.py
- Het script zoekt alle
raw_messagesmetcontains_link = true, detecteert unieke URLs per bericht en slaat voor iedere link metadata op inresources(status,domain,title,extracted_text,final_url). Mislukte downloads krijgenstatus='failed'zodat je ze later opnieuw kunt proberen.
Deze stap classificeert iedere raw_message en gekoppelde resources technisch en haalt metadata + tekst op zonder gebruik te maken van de OpenAI API. Het script maakt eerst automatisch resource-records aan voor:
- plain text berichten (URL
telegram://message/<id>; platformtelegram; formatplain_text); - elke unieke link in
raw_messages.text(duplicate raw_message_id+url combinaties worden overgeslagen en alspendinggelabeld).
Ondersteunde content_format waarden:
plain_text(telegrambericht zonder URL)web_articlegeneric_webpageinstagram_postinstagram_reelyoutube_videoyoutube_shortlinkedin_postunknown_url(fallback)
Uitvoer:
platform,content_format,extraction_methodtitle,description,canonical_url,authorraw_metadata_json,extraction_status,extraction_error
Run het script:
python scripts/process_extraction.pyBatchoutput toont processed/success/partial/failed/skipped én hoeveel resources zijn aangemaakt voordat er geëxtraheerd wordt. Als je recente schemawijzigingen hebt (nieuwe velden in resources), verwijder dan knowledge.db en draai python scripts/init_db.py opnieuw voordat je het script uitvoert.
process_extraction.py voert bij elke run ook een herclassificatie uit voor bestaande LinkedIn- en YouTube-links (bijvoorbeeld eerder opgeslagen youtube.com/shorts/...). Als de automatische detectie een nieuw format aanwijst, worden de resources opnieuw op pending gezet zodat de extractors frisse metadata kunnen ophalen.
De weekly-digest pipeline gebruikt uitsluitend raw_messages en resources (deterministische extraction) om een rapport te bouwen en op te slaan in weekly_reports. Omdat er nieuwe kolommen zijn toegevoegd, verwijder knowledge.db en voer python scripts/init_db.py uit als je vanuit een oudere versie komt.
Gebruik scripts/generate_weekly_report.py om handmatig een rapport te maken:
python scripts/generate_weekly_report.py --user-id 1 --days 7 --forceOptioneel kun je een specifieke periode aangeven met --start-date en --end-date of een preview wegschrijven met --preview-file output.txt. De e-mail bevat nu een coachende intro, highlights (mix van links en notities), thema’s, letterlijke eigen notities, een compacte links-en-bronnen sectie, directieve actiepunten, reflectie en meta-analyse.
Wanneer er al een rapport bestaat (status generated), verstuur je het met:
python scripts/send_weekly_report.py --report-id 3
# of het meest recente voor een gebruiker:
python scripts/send_weekly_report.py --latest --user-id 1Gebruik --force om een eerder verzonden rapport opnieuw te sturen. SMTP-configuratie komt uit .env (SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, EMAIL_FROM, EMAIL_TO).
Zonder --to probeert het script automatisch te verzenden naar de geverifieerde e-mail van de report-user.
scripts/run_weekly_digest.py combineert beide stappen:
python scripts/run_weekly_digest.py --user-id 1 --days 7 --force
# of voor alle geverifieerde users:
python scripts/run_weekly_digest.py --all-verified --days 7 --forceGebruik --dry-run om alleen te genereren en de e-mailtekst te printen zonder te versturen.
highlights_json: recent succesvolle resourcesthemes_json: top platforms/content_format & domeinenideas_json: losse notities/berichten zonder linkactions_json: aanbevelingen/links om op te volgenreflectionenmeta_analysis: regelgebaseerde feedback, inclusief vergelijking met de vorige week indien beschikbaar
Rapporten blijven historisch opgeslagen; er zijn statusvelden (generated, sent) plus generated_at/sent_at. Hierdoor kun je lokaal meerdere weken aanmaken en later automatiseren.
Digest-opbouw gebruikt nu extra heuristieken via app/services/rules_engine.py:
- tweetalige topic-classificatie (NL + EN), o.a.
ai,legal_tech,productivity,business,knowledge_management - item type classificatie:
idea,task,question,reflection,resource,inspiration,note,other - deterministische
importance_score(0-10) - signalen voor
is_consumptionvsis_thinking - signalen voor
content_depth(short_form,long_form,unknown)
Deze signalen worden gebruikt in highlights/thema’s/actiepunten/reflectie, zodat digestkwaliteit verbetert zonder modelkosten.
- De deterministische pipeline blijft leidend.
- Na heuristische digest-opbouw kan optioneel één OpenAI-call de formulering aanscherpen.
- Nieuwe variabelen:
OPENAI_DIGEST_ENABLED=true|false(default:false)OPENAI_DIGEST_MODEL=gpt-4o-miniOPENAI_DIGEST_MAX_INPUT_TOKENS=6000OPENAI_DIGEST_MAX_OUTPUT_TOKENS=1000OPENAI_DAILY_TOKEN_LIMIT=50000OPENAI_DAILY_CALL_LIMIT=5
- Bij API-fouten of limietbereik valt het systeem automatisch terug op de bestaande heuristische digest zonder crash.
Op macOS kun je de scripts automatisch laten draaien via launchd. In deze repo staan twee shell-scripts:
| Script | Beschrijving | Default log |
|---|---|---|
run_daily_digest.sh |
Draait run_weekly_digest.py --days 1 --force en stuurt dus de dagmail (tot 21:00). |
logs/daily.log + daily_launchd.{out,err}.log |
run_digest.sh |
Draait run_weekly_digest.py --days 7 --force en stuurt de wekelijkse zondagmail. |
logs/weekly.log + weekly_launchd.{out,err}.log |
Bijbehorende LaunchAgents staan in ~/Library/LaunchAgents/:
| Plist | Tijdstip | StartCalendarInterval |
|---|---|---|
com.oliviervos.dailydigest.plist |
Dagelijks 21:00 | Hour=21, Minute=0 |
com.oliviervos.weeklydigest.plist |
Zondag 11:00 | Weekday=0, Hour=11, Minute=0 |
Herstarten of testen:
launchctl unload ~/Library/LaunchAgents/com.oliviervos.dailydigest.plist
launchctl load ~/Library/LaunchAgents/com.oliviervos.dailydigest.plist
launchctl kickstart -k gui/$(id -u)/com.oliviervos.dailydigestPas de ProgramArguments aan als je naar een andere map wilt verwijzen. Op andere platformen kun je dezelfde scripts via cron of een scheduler laten lopen.
Naast de CLI kun je nu een lichte FastAPI-server draaien die zowel een dashboard als webhook endpoints aanbiedt. Vereiste extra dependencies staan al in requirements.txt (fastapi, uvicorn, jinja2, python-multipart).
uvicorn app.main:app --reload --port 8000Open daarna http://localhost:8000:
- Dashboard (
/) – toont rapporten, recente input/resources en onboarding-status van users. - Reports (
/reports) – overzicht van recente rapporten. - Report detail (
/reports/{id}) – onderwerp, periodes, e-mail body + JSON samenvattingen. - Acties –
POST /run/daily,/run/weekly,/run/extraction(via HTML forms of direct HTTP-calls).
Alle routes hergebruiken DigestService, EmailService en ExtractionService; er is geen duplicate logica.
Dashboard- en run-routes zijn beveiligd met Basic Auth; gebruik DASHBOARD_USERNAME en DASHBOARD_PASSWORD.
Als er geen activiteit is (0 berichten + 0 bronnen) wordt het rapport wel opgeslagen maar krijgt status skipped_empty en er wordt geen mail gestuurd.
Daily/weekly runs via dashboard of /internal/run/* verwerken alle users met is_active=true, email_verified=true en een ingevuld e-mailadres.
Het endpoint POST /telegram/webhook accepteert reguliere Telegram update payloads. Om lokaal te testen:
# start de FastAPI server eerst
# expose lokaal via bijv. cloudflared of ngrok
cloudflared tunnel --url http://localhost:8000
# daarna
curl \"https://api.telegram.org/bot<token>/setWebhook?url=https://<tunnel-domain>/telegram/webhook\"Nieuwe berichten worden via TelegramService.store_from_payload verwerkt (inclusief onboarding, e-mailverificatie en optionele contact-share), waardoor polling optioneel blijft.
Webhook en polling gebruiken dezelfde verwerkingslogica; /start, e-mailprompt, verificatiecodes, /resend en /export werken dus in beide modi.
DEFAULT_USER_IDbepaalt de fallback user voor handmatige scripts.DASHBOARD_USERNAME/DASHBOARD_PASSWORDvoor Basic Auth.INTERNAL_TRIGGER_TOKENvoor de beveiligde/internal/run/*endpoints (gebruik je ook in GitHub Actions).EMAIL_VERIFICATION_CODE_TTL_MINUTESbepaalt hoe lang onboardingcodes geldig zijn.- SMTP/Telegram variabelen blijven verder hetzelfde.
- Datums/timestamps blijven in UTC opgeslagen in de database; weergave gebeurt in
APP_TIMEZONE.
Gebruik desgewenst render.yaml (in deze repo) als Render Blueprint, of configureer handmatig:
- GitHub klaarzetten
- Fork/clone deze repo en push naar je eigen GitHub.
- Zet in GitHub → Settings → Secrets and variables → Actions:
APP_BASE_URL= publieke Render URL (bijv.https://knowledge-inbox.onrender.com).INTERNAL_TRIGGER_TOKEN= willekeurige secret, gelijk aan de Render env var.
- Render Postgres
- Maak in Render een gratis Postgres database (of laat
render.yamldit voor je doen). - Kopieer de connection string → gebruik als
DATABASE_URL.
- Maak in Render een gratis Postgres database (of laat
- Render Web Service
- Maak een Web Service vanuit je repo (of importeer
render.yaml). - Build command:
pip install -r requirements.txt - Start command:
uvicorn app.main:app --host 0.0.0.0 --port $PORT - Plan: Free.
- Zet de env vars:
DATABASE_URLTELEGRAM_BOT_TOKENSMTP_HOST,SMTP_PORT,SMTP_USERNAME,SMTP_PASSWORDEMAIL_FROM,EMAIL_TOEMAIL_VERIFICATION_CODE_TTL_MINUTESAPP_TIMEZONE(bijv.Europe/Amsterdam)DEFAULT_USER_ID(meestal1)DASHBOARD_USERNAME,DASHBOARD_PASSWORDINTERNAL_TRIGGER_TOKEN
- Maak een Web Service vanuit je repo (of importeer
- Eerste database init
- Open de Render Shell (of background job) en voer één keer uit:
python scripts/init_db.py python scripts/migrate_user_onboarding.py
- Hierdoor worden alle tabellen in Postgres aangemaakt. Doe dit alleen bij de eerste deploy of na schemawijzigingen.
- Open de Render Shell (of background job) en voer één keer uit:
- Telegram webhook koppelen
curl "https://api.telegram.org/bot<TELEGRAM_BOT_TOKEN>/setWebhook?url=https://your-app.onrender.com/telegram/webhook" - Smoke tests
/healthmoet{"ok": true}teruggeven.- Bezoek
https://your-app.onrender.com/en log in met Basic Auth. - Gebruik het dashboard om een test-daily run te starten (zorg dat er minstens één message is).
- GitHub Actions controleren
- In GitHub → Actions → kies
Trigger Daily DigestofTrigger Weekly Digest→ Run workflow. - Controleer dat de run een 2xx-response toont en dat er een report in Render/DB verschijnt.
- In GitHub → Actions → kies
- Dagelijkse/wekelijkse planning
- Workflows draaien automatisch volgens de gedefinieerde cron (UTC). Als de app een
skipped_emptystatus teruggeeft, is dat normaal bij geen activiteit.
- Workflows draaien automatisch volgens de gedefinieerde cron (UTC). Als de app een
Er staan twee workflows in .github/workflows/:
| Workflow | Endpoint | Schedule (UTC) | Doel |
|---|---|---|---|
daily-digest.yml |
/internal/run/daily |
0 20 * * * |
Dagelijkse digest (ongeveer 21:00 NL-tijd) |
weekly-digest.yml |
/internal/run/weekly |
0 9 * * 0 |
Zondagmail (11:00 NL-tijd) |
Zet in GitHub → Settings → Secrets and variables → Actions:
APP_BASE_URL→ bv.https://your-app.onrender.comINTERNAL_TRIGGER_TOKEN→ dezelfde waarde als in Render.env
De workflows posten naar de interne endpoints en falen automatisch als de status geen 2xx is. Je kunt ze ook handmatig runnen via workflow_dispatch. Voor een extraction-backfill kun je een kopie van de daily workflow maken en de URL veranderen naar /internal/run/extraction.
De FastAPI-app is stateless en kan op iedere gratis container/Functions host draaien. Mogelijke routes:
- Cloudflare Pages Functions – bundel met
uvicornen gebruik hun Python support (alpha) + SQLite file in KV/D1, of migreer naar Cloudflare D1. - Railway / Render / Fly.io (free tier) – draai
uvicorn app.main:app --host 0.0.0.0 --port $PORTmet een lichte container. Gebruik Cloudflare Tunnel of Worker als publiek HTTPS-endpoint voor de Telegram webhook. - Deta Space / HuggingFace Spaces – ook mogelijk zolang Python + uvicorn ondersteund wordt.
Omdat alles in één proces zit (FastAPI + services + SMTP), is deployment een kwestie van deze environment variables meegeven en uvicorn starten.
- Laat
.envop SQLite staan (DATABASE_URL=sqlite:///./knowledge.db) en gebruikuvicorn app.main:app --reload. - Scripts (
python scripts/run_bot.py,process_extraction.py,run_weekly_digest.py, enz.) blijven werken zoals eerder. - Launchd/cron zijn optioneel zodra GitHub Actions & Render draaien, maar je kunt ze lokaal blijven gebruiken.
- Tests zoals
sqlite3 knowledge.dbblijven identiek; er zijn geen Postgres-only wijzigingen.
- Voeg repositories en services toe voor het verwerken van binnenkomende data.
- Integreer Telegram, OpenAI en e-mail zodra de basis getest is.
Gebruik de AI verrijkingsstap om knowledge_items automatisch te vullen.
- Zorg dat je
.envde OpenAI variabelen bevat (OPENAI_API_KEY,OPENAI_MODEL). - Installeer de nieuwste requirements (zie boven).
- Verwerk openstaande berichten:
python scripts/process_enrichment.py
- Het script meldt hoeveel items succesvol, gefaald of overgeslagen zijn. Nieuwe
knowledge_itemskoppelen automatischuser_id,raw_message_id, categorie, samenvatting, inzichten, tags en prioriteit.
Gebruik daarna de SQL-checks uit de multi-user sectie om te bevestigen dat knowledge_items gevuld zijn.
- Extraction (dit hoofdstuk) is deterministisch, gebruikt alleen httpx/readability/BeautifulSoup en vult
resourcesmet technische metadata. - Enrichment (dit hoofdstuk) gebruikt OpenAI om inhoudelijk te classificeren en
knowledge_itemste vullen op basis van de ge-normaliseerde data uitresources.
- Telegram intake (
python scripts/run_bot.py) omraw_messageste vullen. - Link backlog verwerken (
python scripts/process_backlog.py) zodatresourcesbestaan voor URL's. - Deterministische extraction (
python scripts/process_extraction.py) om platform/content_format/titel/tekst vast te leggen. - AI enrichment (
python scripts/process_enrichment.py) - deze stap gebruikt zowel de oorspronkelijke message-tekst als de ge-extraheerde resourcecontext.
Internal endpoints geven JSON terug:
status:sent,skipped_empty, offailedok: booleanmessage: samenvatting voor logsreport_id,start,endindien relevant
GitHub Actions falen automatisch bij non-2xx responses (curl exit). De response body wordt kort gelogd.