tmlnv31 downloadsSync messages from Telegram into your vault through a self-hosted Supabase backend.
Sync messages from a Telegram bot into your Obsidian vault. Messages arrive in real time, are stored in Supabase, and are pulled into your vault as Markdown notes — with support for files, forum topics, message edits, and flexible routing rules.
Telegram → Bot → Supabase Edge Function → Postgres → Obsidian plugin → Vault
telegram-webhook)..md files.From the Supabase dashboard → Project Settings → API:
https://abcdefgh.supabase.coanon key under "Project API keys"service_role key (keep this secret)From Project Settings → General:
abcdefgh)This key encrypts your Telegram bot token before it is stored in the database. Generate a random 32-byte base64 key:
openssl rand -base64 32
Save the output — you will need it in the next step.
In the Supabase dashboard → Edge Functions → Manage secrets, add:
| Secret name | Value |
|---|---|
BOT_TOKEN_ENCRYPTION_KEY |
The base64 key you just generated |
The Supabase built-in secrets (SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY) are injected automatically — you do not need to add them.
Clone this repository and link it to your Supabase project:
git clone <repo-url>
cd obsidian-telegram-bridge
npm install
supabase login
supabase link --project-ref YOUR_PROJECT_REF
supabase db push
This applies all migrations and creates the required tables, functions, indexes, and RLS policies.
Some migrations use pg_cron and pg_net. Enable them in the Supabase dashboard:
Database → Extensions → search for and enable:
pg_netpg_cronIf the migrations already ran before you enabled the extensions, re-run supabase db push or run the last two migrations again.
supabase functions deploy setup-bot --no-verify-jwt
supabase functions deploy telegram-webhook --no-verify-jwt
supabase functions deploy usage-warning-check --no-verify-jwt
Verify all three show as Active in the dashboard under Edge Functions.
@BotFather./newbot and follow the prompts.bot).1234567890:ABCdef...The bot can sync messages from:
/start)The bot only sees messages sent after it joined. It does not have access to message history.
By default, bots only see messages that mention them. To let the bot see all messages in a group, disable privacy mode:
/mybots → select your bot → Bot Settings → Group Privacy → Turn off.Until this plugin is published to the Obsidian community registry, install it manually:
Build the plugin:
npm install
npm run build
Copy the output files into your vault's plugin folder:
<vault>/.obsidian/plugins/telegram-bridge/
├── main.js
├── manifest.json
└── styles.css
In Obsidian → Settings → Community plugins, enable Telegram Bridge.
In the plugin settings:
In the plugin settings under Telegram bot:
This registers the webhook with Telegram so messages flow into your Supabase project. On success you will see your bot's username confirmed in the settings.
The bot token is never stored in plain text. It is encrypted with AES-GCM before being saved to the database.
Send a message to your bot (or in your group/channel). Within 30 seconds (or instantly if Realtime is enabled) a .md file should appear in your vault under Telegram/.
Controls where messages are saved. Default:
Telegram/{{chat}}/{{topic}}{{messageDate:YYYY-MM-DD HH-mm-ss}}-{{messageId}}.md
Controls the content rendered inside each note. Default:
- {{messageDate:YYYY-MM-DD HH:mm:ss}} {{user}}
- Chat: {{chat}}
- Type: {{messageType}}
{{content}}
Controls where media attachments are saved. Default:
Telegram/files/{{chat}}/{{file:name}}.{{file:extension}}
| Variable | Description |
|---|---|
{{chat}} |
Chat title or numeric chat ID |
{{chatId}} |
Numeric chat ID |
{{topic}} |
topic-name/ for forum topics, empty otherwise |
{{topicId}} |
Numeric topic ID or empty |
{{user}} |
@username or full name of the sender |
{{messageId}} |
Telegram message ID |
{{messageType}} |
text, photo, document, video, audio, voice, caption, service |
{{content}} |
Full message text or caption |
{{content:N}} |
First N characters of content |
{{messageDate:FORMAT}} |
Message timestamp — use YYYY, MM, DD, HH, mm, ss |
{{file:name}} |
File name without extension |
{{file:extension}} |
File extension |
Rules let you route messages to different notes or folders based on chat, topic, sender, or content.
Rules are checked top to bottom. The first matching rule wins. If no rule matches, the message is dropped.
| Syntax | Meaning |
|---|---|
{{all}} |
Match every message |
{{chat=Name}} |
Chat title or ID equals "Name" (case-insensitive) |
{{chat!=Name}} |
Chat title or ID does not equal "Name" |
{{chat~word}} |
Chat title contains "word" |
{{topic=General}} |
Topic name or ID equals "General" |
{{user=alice}} |
Sender username or name equals "alice" |
{{content~todo}} |
Message text contains "todo" |
Combine multiple conditions in one filter query — all must match (AND logic):
{{chat=Work}}{{topic~Project}}
Rule 1: {{chat=Ideas}}
Note path: Ideas/{{messageDate:YYYY-MM-DD}}.md
Rule 2: {{chat=Work}}{{topic~Standup}}
Note path: Work/Standup/{{messageDate:YYYY-MM-DD}}.md
Rule 3: {{all}}
Note path: Telegram/{{chat}}/{{messageDate:YYYY-MM-DD HH-mm-ss}}-{{messageId}}.md
| Setting | Behaviour |
|---|---|
| Poll interval | Plugin checks for new messages every N seconds (default: 30) |
| Realtime enabled | Supabase Realtime triggers an immediate poll on new messages |
Realtime gives near-instant delivery but uses a persistent WebSocket connection. Keep it off if you want lower resource usage.
The plugin estimates your Supabase usage (database rows + file storage) and can send you a Telegram message when you approach a configured limit.
| Setting | Description |
|---|---|
| Storage limit (MB) | Soft limit used for the estimate (default: 1024 MB) |
| Warning threshold (%) | Warn when usage exceeds this percentage (default: 80%) |
| Telegram warnings | Send the warning via your connected bot |
The warning is sent at most once per threshold crossing and resets automatically when usage drops below the threshold.
This can happen if your Supabase project uses asymmetric JWT signing (ES256) and the Edge Functions are deployed with verify_jwt = true. The fix is to deploy the functions with verify_jwt = false — auth is enforced inside the function via the Bearer token. See step 1.7.
Each Obsidian installation gets a unique client_id stored in the plugin's data.json. If you copy a vault or reinstall the plugin, a new ID may be generated, leaving an old row behind. Old rows are harmless but can be deleted from the sync_clients table in the Supabase dashboard.
Check that the telegram-files storage bucket exists in your Supabase project (created by the initial migration). If the Edge Function lacks permission to write to storage, check the service role key is correctly set in the function secrets.
The Telegram Bot API limits file downloads to 20 MB. Anything larger cannot be pulled by the bot at all — this is a hard limit on Telegram's side, not a plugin setting. The telegram-webhook function detects oversized files, records the message with its file metadata (name, size, mime type) but file_path stays empty, and the webhook returns 200 so Telegram stops retrying. To bypass the 20 MB ceiling you would need a self-hosted local Bot API server (download limit ~2 GB) and point the bot at it instead of api.telegram.org.
obsidian-telegram-bridge/
├── manifest.json Obsidian plugin manifest
├── versions.json Plugin → min Obsidian app version map
├── styles.css Plugin styles
├── esbuild.config.mjs Build config
├── tsconfig.json TypeScript config
├── src/
│ ├── main.ts Plugin entry point
│ ├── sync-engine.ts Polling, cursor management, Realtime
│ ├── vault-writer.ts File creation and editing in the vault
│ ├── message-renderer.ts Markdown rendering with block markers
│ ├── template-engine.ts Template variable expansion
│ ├── distribution-rules.ts Filter query evaluation
│ ├── settings-tab.ts Settings UI
│ └── types.ts Shared TypeScript types
├── test/ Vitest unit tests
├── supabase/
│ ├── config.toml Local dev config
│ ├── functions/
│ │ ├── telegram-webhook/ Receives messages from Telegram
│ │ ├── setup-bot/ Registers webhook, encrypts token
│ │ └── usage-warning-check/ Sends storage warnings via Telegram
│ └── migrations/ Postgres schema migrations (apply in order)
├── scripts/ Bootstrap and verification scripts
└── docs/ Architecture and planning notes