Project 02SoftwareLiveSince October 2026

Moonraker, a Facebook Marketplace bot

A bot that watches one Warhammer Facebook group for Drukhari listings and sends alerts to my phone.

Keywords watched
13
Check interval
~45
min
Posts seen per check
5–10
Group size
36,000
members
Source files
1
Python

What it is

I collect Drukhari, also known as Dark Eldar, a Warhammer 40,000 army. Many of the listings I want appear in a Facebook buy, swap and sell group with about 36,000 members. I wanted to know the moment one appeared, without refreshing the group.

Moonraker watches the Warhammer Australia Buy/Swap/Sell group for keywords and sends a Telegram alert to my iPhone. I built it for myself.

It’s the first of three versions:

  1. Moonraker. Live, and stable at v1.8. A proof of concept for one group.
  2. Starscraper. In development. It will search Facebook Marketplace as a whole, for any kind of item.
  3. Voidwalker. A concept. One bot that watches multiple services.

How it’s built

Moonraker runs in Docker on a Synology DS918+ (Celeron J3455) through Container Manager.

The whole bot is one Python file, warhammer_bot.py. Playwright drives a headless Chromium browser, and Telegram’s Bot API (polling) delivers the alerts. There’s no LLM in it.

Logging in. I export cookies from Edge with export_cookies.py into fb_cookies.json. If the cookies fail, the bot tries to log in again on its own.

Reading the group.

  • Each check scrolls with mouse.wheel(), 20 times.
  • Post text is the outermost div[dir='auto'].
  • Post URLs come from intercepting Facebook’s GraphQL responses, with a DOM function, findPostUrl(), filling the gaps.
  • Checks run about every 45 minutes, give or take 5, and pause overnight.
  • Images, video, fonts and CSS are blocked, and the browser restarts every 32 checks.

Matching and alerts.

  • 13 keywords, matched on whole words.
  • Seen posts are stored as MD5 hashes, pruned after 7 days and capped at 5,000.
  • Each alert is tagged WTB, SELLING or POST, with up to 4 prices pulled out by regex.

Smaller scripts handle the rest: url_test.py checks URL extraction inside the container, deploy.sh ships a build, and watchers.json holds the config for each watcher. Price and condition filters (max_price, min_price, condition) are planned for Starscraper, not built.

Numbers. Moonraker sees 5 to 10 posts per check. In testing before I switched to Patchright, checks averaged 6.1 to 6.5 posts and found 2 matches in 143 posts. I haven’t recorded total listings, uptime or running cost. Power here is about 45c/kWh.

What went wrong or changed

  • Facebook throttled me to 4 to 6 posts per check. Refreshing the cookies changed nothing, so I swapped Playwright for Patchright, a fork built to avoid bot detection. I don’t yet know if that fixed it, and Patchright isn’t pinned to a version.
  • Post links have no href in the DOM. I intercept GraphQL instead, with findPostUrl() as backup. I haven’t validated how often it works.
  • Non-ASCII text crashed Chromium inside page.evaluate(). A scan now runs before every delivery.
  • “venom” matched “venomcrawler”. Word-boundary matching fixed it.
  • window.scrollBy didn’t trigger infinite scroll. mouse.wheel() does.
  • Selenium doesn’t support ARM64. I chose Playwright.
  • The NAS CPU is too weak to detect login pages. Cookie auth is the primary route.
  • SCP is unreliable on the NAS. I use File Station for transfers.
  • I said no to two things. I didn’t split the bot into modules, and I didn’t commit a test suite.

If I started again, I’d begin with Patchright, keep secrets in environment variables from day one, and audit the code before shipping. Moving the secrets out of the script is already Phase 1 of the next plan.