A bot that monitors JW.ORG and broadcasts new content to one or more WhatsApp channels.
- 🎬 Latest videos — polls for new videos
- 📜 New articles — polls the JW.ORG 'What's New?' RSS feed
- 🔔 Alerts & announcements — polls for breaking news alerts; edits placeholder alerts when the localized version arrives
- 🔁 Deduplication — tracks sent items per content type across restarts so nothing is sent twice
- 🗂️ Persistent state — saves state per channel to
run/ - 🌍 Multi-channel & i18n — run multiple channels simultaneously, each with their own language and locale
Every 60 seconds, the bot polls JW.ORG for new videos, articles, and alerts in each configured language. Anything new is posted to the matching WhatsApp channel; the unique identifier is written to run/{channelId}.json so the same item is never sent twice — even after a restart.
- Node.js >= 24
- pnpm
- A WhatsApp account that owns or administers all target channels
-
Clone and install
git clone https://github.com/semkeijsper/jw-push cd jw-push pnpm install -
Create a
config.jsonbased onconfig.example.json:cp config.example.json config.json
-
Discover your channel ID by running the bot with
--list-channels:pnpm build && pnpm start --list-channelsOn first launch a QR code will appear in the terminal — scan it with the WhatsApp account that owns the channel. The bot will then print the name and ID of every channel visible to that account, and exit. This session is also saved for subsequent runs.
This action is also triggered automatically if
config.jsonis empty. -
Fill in
config.jsonusing the IDs from the previous step. See Configuration for the meaning of each field. -
Run the bot:
pnpm start
If your channel already has many subscribers, see
--baselinein CLI flags before your first real run — otherwise the bot will broadcast all current JW.ORG content immediately.
config.json (git-ignored) holds all channel configuration. Use config.example.json as a starting point:
{
"channels": [
{
"id": "your_channel_id@newsletter",
"type": "production",
"name": "English",
"langcode": "E",
"locale": "en",
"articleFeedUrl": "https://www.jw.org/en/whats-new/rss/WhatsNewWebArticles/feed.xml"
}
]
}| Field | Description |
|---|---|
id |
WhatsApp channel ID in the format {id}@newsletter. Run pnpm start --list-channels to print all channel IDs for the logged-in account. |
type |
"production" or "development" — only development channels are affected by --force. |
name |
Human-readable label shown in console output. |
langcode |
JW.ORG internal language code (e.g. E for English, O for Dutch). |
locale |
ISO 639-(1/3) language code used in URLs and message formatting (e.g. en, nl). |
articleFeedUrl |
RSS feed URL for the JW.ORG 'What's New?' section in this language. To find it, open jw.org in the target language, navigate to the 'What's New?' section, and click the RSS button in the top-right corner (visible on desktop only). |
Both langcode and locale for each language can be found on jw.org/en/languages. langcode is the short uppercase abbreviation listed under each language; locale is the lowercase language tag found under symbol.
The flags listed below work with both pnpm start and pnpm dev.
| Flag | Behavior |
|---|---|
--list-channels |
Prints the name and ID of every WhatsApp channel visible to this account, then exits. Triggers automatically when config.json is empty. |
--baseline |
Silently marks all current JW.ORG content as already sent without broadcasting anything, then resumes polling normally. Use on a brand-new channel that already has subscribers, or after migrating to a new host. |
--force |
Clears in-memory state and resends all current content. Only affects type: "development" channels — silently ignored on production. Useful for testing message sending and formatting. |
--linux |
Use /usr/bin/chromium instead of bundled Chromium (for Linux servers like Raspberry Pi or Ubuntu). |
pnpm dev # run with tsx (no build step required)
pnpm build # compile TypeScript to dist/
pnpm lint # run ESLint on src/WhatsApp session data lives in .wwebjs_auth/ and persists between runs — delete this folder to force a new QR scan. Per-channel state lives in run/{channelId}.json — delete a channel's file to reset its sent-item tracking (on next start without --baseline, the channel will be filled with current content).
- Add an entry to the
stringsmap insrc/i18n.tswith your locale key:"de": { newVideo: "Neues Video!", newArticle: "Neuer Artikel!", moreInfo: ">> Weitere Informationen auf jw.org/de <<", },
- Add a matching channel entry in
config.jsonusing that locale, the correctlangcode, and the RSS feed URL for the language. - The bot will pick up the new locale automatically. If no entry exists for a locale, it falls back to English.
Built with whatsapp-web.js.
Developed with the help of Claude.