Skip to content

sbouzla/ubertracker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Typing SVG

🛵 ubertracker

Librairie Go headless pour suivre en temps réel l'état d'une commande Uber Eats.

Tests Coverage Go License


🎯 À propos

🚀 ubertracker est une librairie Go sans interface, sans BDD intégrée, conçue pour être embarquée dans n'importe quel projet. Elle interroge l'API privée d'Uber Eats à intervalle adaptatif et expose les mises à jour via un channel Go.

📦 Ce qu'elle fournit :

  • 🔄 Statut temps réel : phase de la commande (PREPARING, COURIER_ASSIGNED, EN_ROUTE, COMPLETED, CANCELLED)
  • ⏱️ ETA dynamique : minutes restantes extraites des backgroundFeedCards
  • 👤 Infos livreur : nom, prénom, téléphone
  • 🔢 Code PIN : code de vérification à la livraison
  • ♻️ Reprise automatique : relance les suivis interrompus après redémarrage
  • 💉 100% injectable : OrderStore, FetchFn, SendEmbedFn sont des interfaces/fonctions — zéro dépendance externe imposée

"Branche-toi, reçois les updates, fais ce que tu veux avec."


🕵️ Découverte de l'API (Reverse Engineering)

Uber Eats ne proposant pas d'API publique pour le suivi des commandes, la logique de cette librairie repose sur une analyse minutieuse du trafic réseau (Reverse Engineering) :

  1. Interception (Charles Proxy) : Utilisation de Charles Proxy avec une configuration SSL Proxying spécifique (installation du certificat racine sur l'appareil) pour déchiffrer le trafic HTTPS en clair entre l'application client et les serveurs d'Uber.
  2. Isolement de l'Endpoint : Identification de l'endpoint privé responsable du rafraîchissement de la carte et du statut, et extraction du format de la requête (méthode, headers critiques, et structure du payload contenant l'UUID de la commande).
  3. Tests & Validation : Rejeu des requêtes isolées sur un client API web (type Postman/Hoppscotch) pour tester les limites du serveur. Cela a permis d'identifier les cookies strictement nécessaires, de comprendre la rotation des tokens, et d'isoler les clés JSON vitales (comme les backgroundFeedCards pour l'ETA) avant d'écrire la moindre ligne de code Go en dur.

🛠️ Stack Technique

💡 Core

Go Goroutines Context

🔗 Dépendances externes

TLS Client Chromedp

🧪 Tests

Tests Lint


⚡ Installation

go get github.com/superselle/ubertracker@latest

🚀 Démarrage rapide

1. Implémenter OrderStore

// Votre type implémente l'interface tracker.OrderStore
type MyStore struct{ db *sql.DB }

func (s *MyStore) GetSnapshot(ctx context.Context, uuid string) (status string, progress int, text string, rawJSON string, err error) { ... }
func (s *MyStore) SaveOrder(ctx context.Context, order tracker.TrackedOrder) error { ... }
func (s *MyStore) GetMessageID(ctx context.Context, uuid string) (string, error) { ... }
func (s *MyStore) GetPendingOrders(ctx context.Context) (map[string]int, error) { ... }
func (s *MyStore) ListResumableOrders(ctx context.Context) ([]tracker.ResumableOrder, error) { ... }

2. Créer le Manager et démarrer un suivi

import "github.com/superselle/ubertracker/tracker"

store := &MyStore{db: db}
manager := tracker.NewManager(store)

// Reprendre les commandes actives après redémarrage
manager.ResumeActiveOrders()

// Démarrer le suivi d'une commande
manager.StartTracking(tracker.OrderIdentity{
    UUID:      "abc-123-uuid",
    ChannelID: "1234567890",
    GuildID:   "9876543210",
    ClientID:  "user_42",
    CuistotID: "user_99",
})

// Consommer les mises à jour dans une goroutine séparée
go func() {
    for update := range manager.UpdateChannel {
        fmt.Printf("[%s] %s — ETA %d min\n", update.LastStatus, update.LastText, update.ETAMinutes)
        // → Envoyer sur Discord, Telegram, Slack, webhook, etc.
    }
}()

// Arrêt propre
defer manager.Shutdown()

3. Injecter une FetchFn personnalisée (optionnel)

// Par défaut, FetchUberJSON est utilisée (nécessite cookies Uber valides)
// Tu peux injecter ta propre fonction pour les tests ou un proxy custom :
myFetch := func(ctx context.Context, uuid string) ([]byte, error) {
    return os.ReadFile("testdata/order_active.json")
}
manager := tracker.NewManager(store, myFetch)

🏗️ Architecture

ubertracker/
├── tracker/
│   ├── interfaces.go    ← OrderStore, ResumableOrder (contrats)
│   ├── models.go        ← TrackedOrder, Response, Order, MapEntity…
│   ├── config.go        ← GlobalCookies (runtime)
│   ├── api.go           ← FetchUberJSON (appel HTTP Uber)
│   ├── browser.go       ← GetFreshCookies (chromedp)
│   ├── parser.go        ← MergeOrderData, ExtractETAFromOrder, detectPhase
│   ├── worker.go        ← Reconcile, StartOrderWorker, AdaptiveInterval
│   ├── manager.go       ← Manager (goroutine pool + UpdateChannel)
│   └── testdata/        ← Fixtures JSON (7 scénarios)
└── tests/
    ├── testutil/        ← Factories, MockStore, MockFetch
    └── tracker/         ← 57 tests black box

Flux d'une mise à jour :

      cookie Uber
          │
          ▼
   ┌─────────────┐   JSON parsé   ┌─────────────┐   TrackedOrder   ┌──────────────────┐
   │  FetchFn    │ ─────────────► │  Reconcile  │ ───────────────► │  UpdateChannel   │
   │  (HTTP)     │                │  (worker)   │                  │  → consommateur  │
   └─────────────┘                └─────────────┘                  └──────────────────┘
                                       │
                              ShouldEmit ? (delta)
                                       │
                                  OrderStore.SaveOrder

📚 API publique

Manager

Méthode Description
NewManager(store, fetchFn?) Crée le manager (fetchFn optionnel)
StartTracking(OrderIdentity) bool Démarre un worker. false si déjà actif
StopTracking(uuid string) Arrête un worker spécifique
ResumeActiveOrders() Recharge les commandes non terminées depuis le store
Shutdown() Arrêt propre de tous les workers, ferme UpdateChannel
UpdateChannel <-chan TrackedOrder Canal de réception des mises à jour

TrackedOrder (données disponibles)

Champ Type Description
LastStatus string Phase Uber (PREPARING, EN_ROUTE, COMPLETED…)
LastProgress int Barre de progression 1-5
LastText string Message lisible ("Le livreur arrive dans 5 min")
ETAMinutes int Minutes restantes (-1 = inconnu)
FullJSONData string JSON brut complet de la commande
MessageID string ID du message Discord associé
UUID / GuildID / ChannelID string Identifiants de routage

Reconcile (bas niveau)

result, err := tracker.Reconcile(ctx, store, uuid, resp)
// result.ShouldEmit  → true si l'état a changé depuis le snapshot
// result.Phase       → phase de la commande
// result.Eta         → ETA extrait
// result.FinalJSON   → JSON à persister

🧪 Lancer les tests

# Tests complets (57 tests, 0 failures)
go test ./... -count=1

# Avec couverture (64.4% sur tracker/)
go test ./tests/tracker/ -count=1 "-coverpkg=github.com/superselle/ubertracker/tracker"

# Race detector
go test ./... -count=1 -race

📦 Cas d'usage

Voir exemples-utilisation.md pour des intégrations complètes : Discord • Telegram • Slack • IFTTT/Zapier • Home Assistant • Grafana • CLI


📄 Licence

MIT — voir LICENSE


About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages