AutoMarket is a Flutter‑based mobile client that receives instant push notifications about newly listed mobile phones from Facebook Marketplace (Nepal).
It works seamlessly with the AutoMarket backend – displaying deals with photos, price, location, and a direct link to the original listing.
The app registers your device token to Firestore, so the backend knows exactly where to send alerts – even when the app is closed.
- Framework: Flutter (Dart)
- Backend Services: Firebase Firestore, Firebase Cloud Messaging (FCM)
- Image Hosting: Cloudinary (optimised URLs)
- State Management: StreamBuilder + real‑time Firestore listeners
- Platforms: Android (min SDK 23), iOS (13+)
- Authentication (optional): Gmail API / Firebase Auth
Diagram: Backend → FCM → Mobile device → Firestore read
- Backend scraper finds a new deal → uploads image to Cloudinary
- Backend writes listing to Firestore (
listingscollection) - Backend sends FCM message with visible notification payload
- Mobile app shows notification (even if terminated)
- On tap, app opens and reads fresh deal data from Firestore
- Deliver instant, visible alerts – users never miss a good deal, even when the app is closed
- Provide a clean deal feed – real‑time Firestore sync with optimised Cloudinary images
- Simplify token management – automatically register and update FCM tokens in Firestore for backend broadcasting
- 🔔 Push notifications – Android & iOS, appears on lock screen / notification shade
- 📱 Deal browser – sorted by newest, with photo, price, location, and direct Marketplace link
- 🔥 Real‑time Firestore – new deals appear instantly without manual refresh
- 🖼️ Cloudinary image optimisation – fast loading even on slow networks
- 🔄 Auto token registration – device FCM token saved to
devicescollection on launch - 🧹 Expired deal cleanup – deals older than 30 days are automatically hidden (backend removes them)
- 🎯 Filtered scope – only iPhones (X to 16 Pro) and Android phones (Samsung, Pixel, OnePlus, etc.)
Backend automation for the AutoMarket deal-alert app.
It is designed to:
- scan Facebook Marketplace with Playwright,
- filter listings by category, keyword, location, and price,
- upload listing photos to Cloudinary,
- save deal metadata to Firebase Firestore,
- send Firebase Cloud Messaging alerts,
- delete Firestore + Cloudinary data older than 30 days.
The default scraper watches Nepal Marketplace locations/categories for mobile devices only:
- iPhones: iPhone X/10, 11, 12, 13, 14, 15, 16, Pro/Max variants by title matching
- Android phones: Samsung, Vivo, Oppo, Realme, Redmi, Xiaomi, Poco, OnePlus, Nothing, Pixel, Huawei, Honor, Infinix, Tecno, Nokia, Motorola
When SCRAPE_DRY_RUN=false, every new matched listing is saved into Firestore and an FCM notification is sent to registered device tokens.
The backend now sends OS-visible FCM notification payloads for Android and iOS, so notifications can appear even when the Flutter app is backgrounded or fully closed, assuming the mobile app and Firebase/APNs setup are correct.
Rotate any Cloudinary secret that has been pasted into chat or committed anywhere. Store secrets only in .env locally and GitHub Actions Secrets in production.
npm install
npx playwright install chromium
cp .env.example .env
npm run validateThen fill .env with your real values.
For Firebase private keys, keep escaped newlines:
FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"npm run scrape
npm run cleanupBy default SCRAPE_DRY_RUN=true, so the scraper logs matched items without writing to Firebase or Cloudinary.
Add these repository secrets:
FIREBASE_PROJECT_IDFIREBASE_CLIENT_EMAILFIREBASE_PRIVATE_KEYCLOUDINARY_CLOUD_NAMECLOUDINARY_API_KEYCLOUDINARY_API_SECRETMARKETPLACE_CITY_URLMARKETPLACE_URLSMARKETPLACE_MAX_TARGETSFCM_TOKENS
See docs/github-automation.md for the full GitHub Actions setup.
Stores marketplace listing metadata.
Flutter app can store FCM device tokens here later:
{
token: "...",
userName: "Mina",
platform: "android",
apnsToken: "...", // optional, useful for iOS debugging
enabled: true,
createdAt: Timestamp
}For terminated-app notifications, backend delivery must include a visible notification payload, not just data.
This backend sends:
- top-level FCM
notification.titleandnotification.body - Android high-priority notification config on channel
marketplace_listing_alerts - iOS APNs alert payload with sound and alert headers
If notifications still only appear inside the app, the remaining problem is usually mobile or Firebase configuration rather than scraper logic.
The cleanup job deletes records where expiresAt <= now, then deletes matching Cloudinary assets using cloudinaryPublicId.
.jpg)