diff --git a/providers/dpd_eu/dpd_eu_provider.go b/providers/dpd_eu/dpd_eu_provider.go new file mode 100644 index 0000000..0469428 --- /dev/null +++ b/providers/dpd_eu/dpd_eu_provider.go @@ -0,0 +1,80 @@ +package dpd_eu + +import ( + "context" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "strings" + "time" + + "github.com/alufers/paczkobot/commondata" + "github.com/alufers/paczkobot/commonerrors" +) + +type DPDEuProvider struct{} + +func (p *DPDEuProvider) GetName() string { + return "dpd-eu" +} + +func (p *DPDEuProvider) MatchesNumber(trackingNumber string) bool { + return true +} + +func (p *DPDEuProvider) Track(ctx context.Context, trackingNumber string) (*commondata.TrackingData, error) { + req, err := http.NewRequestWithContext( + ctx, + "GET", + fmt.Sprintf("https://tracking.dpd.de/rest/plc/en_US/%s", trackingNumber), + nil, + ) + if err != nil { + return nil, fmt.Errorf("failed to create request: %w", err) + } + + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, commonerrors.NewNetworkError(p.GetName(), req) + } + defer res.Body.Close() + if res.StatusCode == http.StatusNotFound { + return nil, commonerrors.NotFoundError + } + if res.StatusCode != 200 { + body, _ := io.ReadAll(res.Body) + log.Printf("DPD ERROR BODY: %v", string(body)) + return nil, fmt.Errorf("HTTP status code %v", res.StatusCode) + } + + decoder := json.NewDecoder(res.Body) + decodedBody := &DpdEuResponse{} + if err := decoder.Decode(decodedBody); err != nil { + return nil, fmt.Errorf("failed to parse json: %w", err) + } + if decodedBody.ParcellifecycleResponse == nil { + return nil, commonerrors.NotFoundError + } + + trackingData := &commondata.TrackingData{ + ProviderName: p.GetName(), + TrackingSteps: []*commondata.TrackingStep{}, + } + + for _, event := range decodedBody.ParcellifecycleResponse.ParcelLifeCycleData.ScanInfo.Scan { + datetime, _ := time.Parse("2006-01-02T15:04:05", event.Date) + location := event.ScanData.Location + dscr := strings.Join(event.ScanDescription.Content, " ") + + trackingData.TrackingSteps = append(trackingData.TrackingSteps, &commondata.TrackingStep{ + Datetime: datetime, + CommonType: commondata.CommonTrackingStepType_UNKNOWN, + Message: dscr, + Location: location, + }) + } + + return trackingData, nil +} diff --git a/providers/dpd_eu/json_schema.go b/providers/dpd_eu/json_schema.go new file mode 100644 index 0000000..7debc15 --- /dev/null +++ b/providers/dpd_eu/json_schema.go @@ -0,0 +1,105 @@ +package dpd_eu + +type DpdEuResponse struct { + ParcellifecycleResponse *ParcellifecycleResponse `json:"parcellifecycleResponse"` +} +type ServiceElements struct { + Label string `json:"label"` + Content []string `json:"content"` +} +type AdditionalProperties struct { + Key string `json:"key"` + Value string `json:"value"` +} +type ShipmentInfo struct { + ParcelLabelNumber string `json:"parcelLabelNumber"` + ServiceElements []ServiceElements `json:"serviceElements"` + SortingCode string `json:"sortingCode"` + ProductName string `json:"productName"` + CodInformationAvailable bool `json:"codInformationAvailable"` + Documents []any `json:"documents"` + AdditionalProperties []AdditionalProperties `json:"additionalProperties"` +} +type Description struct { + Content []string `json:"content"` +} +type Depot struct { + BusinessUnit string `json:"businessUnit"` + Number string `json:"number"` +} +type StatusInfo struct { + Status string `json:"status"` + Label string `json:"label"` + Description Description `json:"description"` + StatusHasBeenReached bool `json:"statusHasBeenReached"` + IsCurrentStatus bool `json:"isCurrentStatus"` + Location string `json:"location,omitempty"` + Depot Depot `json:"depot,omitempty"` + Date string `json:"date,omitempty"` + NormalItems []any `json:"normalItems"` + ImportantItems []any `json:"importantItems"` + ErrorItems []any `json:"errorItems"` +} +type ScanDepot struct { + BusinessUnit string `json:"businessUnit"` + Number string `json:"number"` +} +type ScanType struct { + Name string `json:"name"` + Code string `json:"code"` + DetailMode string `json:"detailMode"` +} +type AdditionalCodes struct { + AdditionalCode []any `json:"additionalCode"` +} + +type ScanDescription struct { + Label string `json:"label"` + Content []string `json:"content"` +} +type DestinationDepot struct { + BusinessUnit string `json:"businessUnit"` + Number string `json:"number"` +} +type ServiceElements2 struct { + Code string `json:"code"` + ShortName string `json:"shortName"` + DetailMode string `json:"detailMode"` +} +type ScanData struct { + ScanDate string `json:"scanDate"` + ScanTime string `json:"scanTime"` + ScanDepot ScanDepot `json:"scanDepot"` + DestinationDepot DestinationDepot `json:"destinationDepot"` + Location string `json:"location"` + ScanType ScanType `json:"scanType"` + Route string `json:"route"` + AdditionalCodes AdditionalCodes `json:"additionalCodes"` + Country string `json:"country"` + ServiceElements []ServiceElements2 `json:"serviceElements"` + SortingCode string `json:"sortingCode"` + Tour string `json:"tour"` + DetailMode string `json:"detailMode"` + InsertTimestamp string `json:"insertTimestamp"` +} +type Scan struct { + Date string `json:"date"` + IntegrationDate string `json:"integrationDate"` + ScanData ScanData `json:"scanData,omitempty"` + AdditionalCodes []any `json:"additionalCodes"` + ScanDescription ScanDescription `json:"scanDescription"` + Links []any `json:"links"` + ProductName string `json:"productName,omitempty"` +} +type ScanInfo struct { + Scan []Scan `json:"scan"` +} +type ParcelLifeCycleData struct { + ShipmentInfo ShipmentInfo `json:"shipmentInfo"` + StatusInfo []StatusInfo `json:"statusInfo"` + ContactInfo []any `json:"contactInfo"` + ScanInfo ScanInfo `json:"scanInfo"` +} +type ParcellifecycleResponse struct { + ParcelLifeCycleData ParcelLifeCycleData `json:"parcelLifeCycleData"` +} diff --git a/providers/provider.go b/providers/provider.go index d9e3036..4b0af02 100644 --- a/providers/provider.go +++ b/providers/provider.go @@ -7,6 +7,7 @@ import ( "github.com/alufers/paczkobot/providers/cainiao" "github.com/alufers/paczkobot/providers/deutsche_post" "github.com/alufers/paczkobot/providers/dhl" + "github.com/alufers/paczkobot/providers/dpd_eu" "github.com/alufers/paczkobot/providers/dpdcompl" "github.com/alufers/paczkobot/providers/fedex_pl" "github.com/alufers/paczkobot/providers/geis_pl" @@ -35,6 +36,7 @@ var AllProviders = []Provider{ &geis_pl.GeisPlProvider{}, &orlen.OrlenProvider{}, &deutsche_post.DeutschePostProvider{}, + &dpd_eu.DPDEuProvider{}, } type Provider interface {