Davis Cat

Automation panel for Facebook

Facebook Automation Panel

Imagine having to perform actions in 200 different Facebook accounts manually—sounds crazy, right? Well, I needed to do that for a project I was working on, so I started this automation project.

Stack

  • Python 3.10 (the tools I used weren’t compatible with newer versions)
  • For ease of use, we made two installable variants: one packaged with Nuitka and one with PyInstaller. I’d prefer Nuitka, as its executables are faster and smaller; they also support Playwright.
  • Tkinter for the GUI
  • Playwright for browser automation
  • Patchright for stealthier browser sessions

For stealthier sessions we used Patchright, a hardened Playwright fork, and persistent context folders. This later became an issue (you’ll see why).

Automating actions

Facebook uses React heavily, so traditional WebDriver-only automations were out of the question. Instead, we used JavaScript injection to perform actions on the page; this allowed us to interact with React components directly.

To behave like real users, one of our goals was to have accounts perform random actions after onboarding. These actions included:

  • Liking posts
  • Commenting on posts
  • Sharing posts
  • Scrolling the feed

There were multiple instances where we had to deal with modal interfaces. Note that in Facebook’s feed, when you open a post it opens in a modal, so we cannot simply interact with the first like, comment, or share button we find—there may be multiple such buttons for different posts in the feed. To interact with the correct one, we first need to detect the modal’s presence.

We implemented a helper that returns the modal’s selector when present; this let us interact with the correct buttons inside the modal.

Login

We implemented a mechanism that automatically used cookies if they existed; if not, it performed a login using stored credentials.

Sometimes a captcha would appear, so we implemented two mechanisms to deal with it:

  • an integration with a captcha solving service to solve captchas automatically
  • manually allowing the account manager to solve the captcha (we also send them a notification)

Anti-detection

Automation is a cat and mouse game. Platforms don’t just look for “bots”; they look for patterns. If your bot moves with 100% efficiency, it’s 100% dead. To make the panel truly “plausible” to their AI, we implemented a layer of behavioral heuristics:

  • CDP Stealth (Chrome DevTools Protocol): Standard Playwright leaves traces (like the navigator.webdriver flag). Using Patchright allowed us to mask these at the binary level, ensuring the browser environment passed “strong” integrity checks.

  • Variable Jitter: We implemented a non-linear delay system. Instead of a standard sleep(2), we used a Gaussian distribution for wait times and mouse movements. This ensures that no two interaction sequences are ever identical.

  • The Network Layer: We decoupled proxy rotation from the browser logic. Each account was tied to a specific residential proxy sticky session, preventing “geographic jumping”—one of the fastest ways to trigger a checkpoint.

Concurrency

To manage multiple accounts concurrently, we used Python’s threading module. Each account ran in its own thread, allowing us to manage many accounts without blocking the main thread.

Each thread handled a session with its own browser context and its own GUI elements, allowing us to monitor each account’s status individually.

After a few seconds of inactivity, the thread would perform random scrolling and then close.

We also considered to go fully async but threading was simpler, as each profile is mostly independent and isolated.

The storage issue

During development we encountered multiple storage issues when working with many accounts.

chromium.launchPersistentContext(...) became a nightmare: we had to manage hundreds of profiles, each requiring its own folder. Even worse, storage usage was huge—a single profile can start at 30 MB and grow up to multiple GBs in minutes.

When using persistent contexts, they don’t just store cookies; they store hundreds of megabytes of telemetry, blobs, and redundant cache. We were starting to run out of disk space quickly.

Moving to non-persistent contexts and loading only cookies was not an option, as Facebook is strict with sessions and often triggers security checks when it detects unusual behavior.

To solve this, we created a custom storage system that cleaned up unnecessary data from each profile’s folder, keeping only the essential files needed for login and session persistence. This drastically reduced storage usage per profile, allowing us to manage hundreds of accounts without running out of disk space.

Data & secrets storage

For data and secrets storage we made two MVPs; one using SQLite and one using JSON files. The JSON file version was easier to debug and maintain, so we went with that.

We also found ourselves and our account managers needing to frequently edit, import, and export account data—the JSON feature helped with this.

We built a simple helper to re-sync missing files and placeholders, for example if an account was imported but its JSON data was missing.

Conclusion

At scale, Facebook automation becomes a systems problem, not a scripting one.

The hard parts were not Playwright or Python, but entropy, storage, and consistency over time. Things that work for a few accounts break fast at hundreds. Stealth came from limits: slower execution, imperfect actions, and just enough state to look human.

The main lesson was restraint. Over-optimizing makes systems fragile. Designing for pauses, waste, and occasional failure made the panel last longer than expected.