BD Rail Ticket Alarm

The short version

The extension is a timer plus a polite watcher. It waits until the exact second your tickets go on sale, asks the ticket website “is my train open yet?” about once a second, and when the answer is yes it rings an alarm and points at the buttons you need to press. It never presses them for you.

It doesn't try to look like a human, and it doesn't try to hide from bot detection. It stays safe by asking rarely, for a short time, only when there's a reason to, and by staying completely out of the site's bot check.

The pieces

FileRuns whereJob
popup.html / popup.jsThe toolbar panelCalendar, route/train/seat form, ARM, countdown, saved trips, history, English/Bangla.
background.jsService workerWorks out the release time, sets chrome.alarms (main + 1-day + 1-hour reminders), opens the site tab ~75 s early, shows notifications, starts/stops the alarm sound.
content.jsInside eticket.railway.gov.bdThe watcher: login check, clock sync, polite search loop, status box, BOOK NOW glow, seat outlining, seat-direction guess.
offscreen.html / offscreen.jsHidden extension pagePlays the siren (Web Audio oscillator).
rail-data.jsPopup, worker, pageBundled station list, train names, and each train's release time and zone (EAST 2 PM / WEST 8 AM).
i18n.jsPopupEnglish and Bangla text.

Timeline of one run

ARM pressed (popup)
  └─ background: opening day = journey date − 10 days
     check 1 = 8:00 AM that day (normal-day opening, every train)
     check 2 = 2:00 PM, East trains only (the Eid-time opening)
     sets alarms at  T−24h (reminder) · T−1h (reminder) · T−75s (launch), T = check 1
     check 1 fires → check 2's alarm is set; seats found → check 2 is cancelled

T−75s   background opens (or reuses) a eticket.railway.gov.bd tab and saves a "mission"
        content.js picks up the mission:
          1. logged in?            no  → alarm + notification, stop
          2. date released inside this run's window?  no → stop (don't search for nothing)
          3. one request for a small static file → read the server's Date header
             → estimate the clock difference (about ±0.5 s)

T−0.5s  search loop: GET /bookings/search-trips-v2   (the same call the site's Search button makes)
          wait 700 ms + random 0–250 ms between searches  (≈ 1 per second)
          429 "too many requests" → wait 2 s, 4 s, 8 s, 15 s …
          401 "logged out"        → alarm + notification, stop
          5 minutes passed        → give up, notify

Seats!  save a short "found" note, send a notification,
        load the site's own results page for that train and class

Reload  content.js sees the note → alarm on, scroll to the train, make BOOK NOW glow
User    clicks BOOK NOW → the site loads the seat map (and runs its own bot check — untouched)
        content.js watches the page (MutationObserver) → outlines the best free seats
        and guesses which way each faces (⬆ / ⬇ / ⬆→⬇)
        ← zero network requests from the extension in this step
User    clicks seats → CONTINUE PURCHASE → OTP → pays

Where the login comes from

You log in on the website as usual. The website keeps your session in the page's localStorage (a token plus a device ID and key). The watcher reads those and sends them with its searches as the same headers the website itself uses (Authorization, X-Device-Id, X-Device-Key). They are only ever sent to the railway's own ticketing API (the one eticket.railway.gov.bd uses), never stored by the extension and never sent anywhere else. Your password is never seen.

Why the alarm plays from a hidden page

Browsers block sound on a page until you click it (the “autoplay policy”). The tab that opens at release time was opened automatically, so it isn't allowed to make noise. Extensions are allowed to play sound from an offscreen document, so the siren runs there instead. That's also what Tools → Test alarm sound uses, so the test is honest.

“Does it act like a human?” — honestly, no

It helps to be precise here, because “mimicking humans” usually means tricks to fool bot detection. This extension doesn't do those, on purpose.

What it does

What it doesn't do

The random gap is not a disguise. A server that looks closely can still tell these requests come from a script: evenly spaced API calls starting at exactly 2:00:00 PM with no clicks on the page. The protection comes from volume and timing — staying inside what the server tolerates — not from hiding.

What it does to avoid getting you blocked

MeasureWhyWhere
Refuses to start before releaseIf the date won't open within this run's window, searching is pointless and only adds up requests. The run stops with a message.content.js · runEngine()
Starts 0.5 s before release, on the server's clockNo early searching “just in case”. Clock offset comes from one HTTP Date header.serverOffsetMs(), startLeadMs
≈ 1 search per second700 ms + 0–250 ms random, plus request time. Configurable (minimum 300 ms) — don't lower it.pollIntervalMs
Backs off on 429When the server says “too frequent”, waits 2 → 4 → 8 → 15 s before trying again.search loop
Stops at the first hitAs soon as your class shows online seats, searching ends.search loop
Hard time limitGives up after 5 minutes (max 15). Worst case ≈ 300 searches.maxWaitMinutes
Stops on 401A logged-out session isn't retried.search loop
Seat help is offlineSeat outlining only reads the page that's already loaded. Switching coaches makes no requests. The seat-direction guess is worked out from the route and the map, with no lookups.suggestSeats()
Never touches the bot checkThe seat-map request needs a Turnstile token. The extension never calls it — you open the seat map with your click.—
STOP button everywherePanel and on-page status box both abort immediately.stopRun

Your side of it: don't test repeatedly on dates that aren't on sale, don't arm two computers for the same trip, and don't open many ticket-site tabs during a run.

Limitations

Drawbacks and risks

Working on the code

  1. Clone the repo. The extension is in the shohoz-autobook/ folder.
  2. brave://extensions → Developer mode → Load unpacked → choose that folder.
  3. After editing, press ↻ on the extension card and reload the ticket-site tab.
  4. Test on a date that's already on sale with ▶ Test run now. Open the status box's details ▾ for the log.

Useful constants

SettingDefaultWhere
Open the tab before release75 sPRELAUNCH_LEAD_MS in background.js
Start searching before release500 msstartLeadMs in background.js defaults
Gap between searches700 ms + 0–250 mspollIntervalMs (panel → Advanced)
Give up after5 minmaxWaitMinutes (panel → Advanced)
“Seats open” handoff stays active15 minASSIST_TTL_MS in content.js
Sale window10 daysSALE_WINDOW_DAYS

Contributing

Issues and pull requests are welcome — especially fixes when the site's layout changes, release-time updates and translation fixes. Changes that get around the bot check, automate login/OTP/payment or raise the request rate won't be accepted.