2015-10-31 16:40:23 +00:00
""" Postal tracking module """
# PYTHON STUFF ############################################
2017-07-08 12:38:24 +00:00
import json
2015-07-13 21:20:10 +00:00
import urllib . parse
from bs4 import BeautifulSoup
2015-10-28 19:55:02 +00:00
import re
2015-07-13 21:20:10 +00:00
from nemubot . hooks import hook
2015-10-30 20:57:45 +00:00
from nemubot . exception import IMException
2017-07-08 12:38:24 +00:00
from nemubot . tools . web import getURLContent , getJSON
2017-08-27 16:22:53 +00:00
from nemubot . module . more import Response
2015-07-13 21:20:10 +00:00
2015-10-27 21:19:12 +00:00
# POSTAGE SERVICE PARSERS ############################################
2015-07-13 21:20:10 +00:00
2015-10-29 01:10:46 +00:00
def get_tnt_info ( track_id ) :
2015-10-30 19:55:02 +00:00
values = [ ]
2017-09-26 00:30:14 +00:00
data = getURLContent ( ' https://www.tnt.fr/public/suivi_colis/recherche/visubontransport.do?bonTransport= %s ' % track_id )
2015-10-29 01:10:46 +00:00
soup = BeautifulSoup ( data )
2015-10-30 19:55:02 +00:00
status_list = soup . find ( ' div ' , class_ = ' result__content ' )
if not status_list :
return None
last_status = status_list . find ( ' div ' , class_ = ' roster ' )
if last_status :
for info in last_status . find_all ( ' div ' , class_ = ' roster__item ' ) :
values . append ( info . get_text ( ) . strip ( ) )
if len ( values ) == 3 :
return ( values [ 0 ] , values [ 1 ] , values [ 2 ] )
2015-10-29 01:10:46 +00:00
2015-07-13 21:20:10 +00:00
def get_colissimo_info ( colissimo_id ) :
2017-09-26 00:21:14 +00:00
colissimo_data = getURLContent ( " https://www.laposte.fr/particulier/outils/suivre-vos-envois?code= %s " % colissimo_id )
2015-07-13 21:20:10 +00:00
soup = BeautifulSoup ( colissimo_data )
2017-09-26 00:21:14 +00:00
dataArray = soup . find ( class_ = ' results-suivi ' )
if dataArray and dataArray . table and dataArray . table . tbody and dataArray . table . tbody . tr :
td = dataArray . table . tbody . tr . find_all ( ' td ' )
if len ( td ) > 2 :
date = td [ 0 ] . get_text ( )
libelle = re . sub ( r ' [ \ n \ t \ r] ' , ' ' , td [ 1 ] . get_text ( ) )
site = td [ 2 ] . get_text ( ) . strip ( )
return ( date , libelle , site . strip ( ) )
2015-07-13 21:20:10 +00:00
2015-10-28 19:55:02 +00:00
2015-09-15 22:23:42 +00:00
def get_chronopost_info ( track_id ) :
data = urllib . parse . urlencode ( { ' listeNumeros ' : track_id } )
2017-09-26 00:30:14 +00:00
track_baseurl = " https://www.chronopost.fr/expedier/inputLTNumbersNoJahia.do?lang=fr_FR "
2017-07-07 04:38:00 +00:00
track_data = getURLContent ( track_baseurl , data . encode ( ' utf-8 ' ) )
2015-09-15 22:23:42 +00:00
soup = BeautifulSoup ( track_data )
infoClass = soup . find ( class_ = ' numeroColi2 ' )
if infoClass and infoClass . get_text ( ) :
info = infoClass . get_text ( ) . split ( " \n " )
if len ( info ) > = 1 :
info = info [ 1 ] . strip ( ) . split ( " \" " )
if len ( info ) > = 2 :
date = info [ 2 ]
libelle = info [ 1 ]
return ( date , libelle )
2015-10-28 19:55:02 +00:00
2015-09-15 22:23:42 +00:00
def get_colisprive_info ( track_id ) :
data = urllib . parse . urlencode ( { ' numColis ' : track_id } )
2017-07-07 04:38:00 +00:00
track_baseurl = " https://www.colisprive.com/moncolis/pages/detailColis.aspx "
track_data = getURLContent ( track_baseurl , data . encode ( ' utf-8 ' ) )
2015-09-15 22:23:42 +00:00
soup = BeautifulSoup ( track_data )
dataArray = soup . find ( class_ = ' BandeauInfoColis ' )
2015-10-28 19:55:02 +00:00
if ( dataArray and dataArray . find ( class_ = ' divStatut ' )
and dataArray . find ( class_ = ' divStatut ' ) . find ( class_ = ' tdText ' ) ) :
status = dataArray . find ( class_ = ' divStatut ' ) \
. find ( class_ = ' tdText ' ) . get_text ( )
2015-09-15 22:23:42 +00:00
return status
2015-10-28 19:55:02 +00:00
2015-07-13 21:20:10 +00:00
def get_laposte_info ( laposte_id ) :
2015-09-15 22:23:42 +00:00
data = urllib . parse . urlencode ( { ' id ' : laposte_id } )
laposte_baseurl = " http://www.part.csuivi.courrier.laposte.fr/suivi/index "
2017-07-07 04:38:00 +00:00
laposte_data = getURLContent ( laposte_baseurl , data . encode ( ' utf-8 ' ) )
2015-07-13 21:20:10 +00:00
soup = BeautifulSoup ( laposte_data )
search_res = soup . find ( class_ = ' resultat_rech_simple_table ' ) . tbody . tr
if ( soup . find ( class_ = ' resultat_rech_simple_table ' ) . thead
2015-09-15 22:23:42 +00:00
and soup . find ( class_ = ' resultat_rech_simple_table ' ) . thead . tr
and len ( search_res . find_all ( ' td ' ) ) > 3 ) :
2015-07-13 21:20:10 +00:00
field = search_res . find ( ' td ' )
poste_id = field . get_text ( )
field = field . find_next ( ' td ' )
poste_type = field . get_text ( )
field = field . find_next ( ' td ' )
poste_date = field . get_text ( )
field = field . find_next ( ' td ' )
poste_location = field . get_text ( )
field = field . find_next ( ' td ' )
poste_status = field . get_text ( )
2015-09-15 22:23:42 +00:00
2015-10-28 19:55:02 +00:00
return ( poste_type . lower ( ) , poste_id . strip ( ) , poste_status . lower ( ) ,
poste_location , poste_date )
2015-07-13 21:20:10 +00:00
2015-09-17 23:23:42 +00:00
2017-07-02 17:08:01 +00:00
def get_postnl_info ( postnl_id ) :
data = urllib . parse . urlencode ( { ' barcodes ' : postnl_id } )
postnl_baseurl = " http://www.postnl.post/details/ "
2017-07-07 04:38:00 +00:00
postnl_data = getURLContent ( postnl_baseurl , data . encode ( ' utf-8 ' ) )
2017-07-02 17:08:01 +00:00
soup = BeautifulSoup ( postnl_data )
if ( soup . find ( id = ' datatables ' )
and soup . find ( id = ' datatables ' ) . tbody
and soup . find ( id = ' datatables ' ) . tbody . tr ) :
search_res = soup . find ( id = ' datatables ' ) . tbody . tr
if len ( search_res . find_all ( ' td ' ) ) > = 3 :
field = field . find_next ( ' td ' )
post_date = field . get_text ( )
field = field . find_next ( ' td ' )
post_status = field . get_text ( )
field = field . find_next ( ' td ' )
post_destination = field . get_text ( )
return ( post_status . lower ( ) , post_destination , post_date )
2017-08-10 04:48:48 +00:00
def get_usps_info ( usps_id ) :
usps_parcelurl = " https://tools.usps.com/go/TrackConfirmAction_input? " + urllib . parse . urlencode ( { ' qtc_tLabels1 ' : usps_id } )
usps_data = getURLContent ( usps_parcelurl )
soup = BeautifulSoup ( usps_data )
if ( soup . find ( class_ = " tracking_history " )
and soup . find ( class_ = " tracking_history " ) . find ( class_ = " row_notification " )
and soup . find ( class_ = " tracking_history " ) . find ( class_ = " row_top " ) . find_all ( " td " ) ) :
notification = soup . find ( class_ = " tracking_history " ) . find ( class_ = " row_notification " ) . text . strip ( )
date = re . sub ( r " \ s+ " , " " , soup . find ( class_ = " tracking_history " ) . find ( class_ = " row_top " ) . find_all ( " td " ) [ 0 ] . text . strip ( ) )
status = soup . find ( class_ = " tracking_history " ) . find ( class_ = " row_top " ) . find_all ( " td " ) [ 1 ] . text . strip ( )
last_location = soup . find ( class_ = " tracking_history " ) . find ( class_ = " row_top " ) . find_all ( " td " ) [ 2 ] . text . strip ( )
print ( notification )
return ( notification , date , status , last_location )
2017-07-08 12:38:24 +00:00
def get_fedex_info ( fedex_id , lang = " en_US " ) :
data = urllib . parse . urlencode ( {
' data ' : json . dumps ( {
" TrackPackagesRequest " : {
" appType " : " WTRK " ,
" appDeviceType " : " DESKTOP " ,
" uniqueKey " : " " ,
" processingParameters " : { } ,
" trackingInfoList " : [
{
" trackNumberInfo " : {
" trackingNumber " : str ( fedex_id ) ,
" trackingQualifier " : " " ,
" trackingCarrier " : " "
}
}
]
}
} ) ,
' action ' : " trackpackages " ,
' locale ' : lang ,
' version ' : 1 ,
' format ' : " json "
} )
fedex_baseurl = " https://www.fedex.com/trackingCal/track "
fedex_data = getJSON ( fedex_baseurl , data . encode ( ' utf-8 ' ) )
if ( " TrackPackagesResponse " in fedex_data and
" packageList " in fedex_data [ " TrackPackagesResponse " ] and
2017-08-09 20:53:35 +00:00
len ( fedex_data [ " TrackPackagesResponse " ] [ " packageList " ] ) and
2017-09-26 00:25:14 +00:00
( not fedex_data [ " TrackPackagesResponse " ] [ " errorList " ] [ 0 ] [ " code " ] or
fedex_data [ " TrackPackagesResponse " ] [ " errorList " ] [ 0 ] [ " code " ] == ' 0 ' ) and
2017-08-09 20:53:35 +00:00
not fedex_data [ " TrackPackagesResponse " ] [ " packageList " ] [ 0 ] [ " errorList " ] [ 0 ] [ " code " ]
2017-07-08 12:38:24 +00:00
) :
return fedex_data [ " TrackPackagesResponse " ] [ " packageList " ] [ 0 ]
2017-08-09 22:55:13 +00:00
def get_dhl_info ( dhl_id , lang = " en " ) :
dhl_parcelurl = " http://www.dhl.com/shipmentTracking? " + urllib . parse . urlencode ( { ' AWB ' : dhl_id } )
dhl_data = getJSON ( dhl_parcelurl )
if " results " in dhl_data and dhl_data [ " results " ] :
return dhl_data [ " results " ] [ 0 ]
2015-10-27 21:19:12 +00:00
# TRACKING HANDLERS ###################################################
2015-09-17 23:23:42 +00:00
2015-10-29 01:10:46 +00:00
def handle_tnt ( tracknum ) :
info = get_tnt_info ( tracknum )
if info :
2015-10-30 19:55:02 +00:00
status , date , place = info
placestr = ' '
if place :
placestr = ' à \x02 {place} \x0f '
2015-10-29 01:10:46 +00:00
return ( ' Le colis \x02 {trackid} \x0f a actuellement le status: '
2015-10-30 19:55:02 +00:00
' \x02 {status} \x0F mis à jour le \x02 {date} \x0f {place} . '
. format ( trackid = tracknum , status = status ,
date = re . sub ( r ' \ s+ ' , ' ' , date ) , place = placestr ) )
2015-10-29 01:10:46 +00:00
2015-10-27 21:19:12 +00:00
def handle_laposte ( tracknum ) :
info = get_laposte_info ( tracknum )
2015-09-17 23:23:42 +00:00
if info :
2015-10-27 21:19:12 +00:00
poste_type , poste_id , poste_status , poste_location , poste_date = info
2015-10-28 19:55:02 +00:00
return ( " Le courrier de type \x02 %s \x0F : \x02 %s \x0F est actuellement "
" \x02 %s \x0F dans la zone \x02 %s \x0F (Mis à jour le \x02 %s \x0F "
" ). " % ( poste_type , poste_id , poste_status ,
poste_location , poste_date ) )
2015-09-17 23:23:42 +00:00
2017-07-02 17:08:01 +00:00
def handle_postnl ( tracknum ) :
info = get_postnl_info ( tracknum )
if info :
post_status , post_destination , post_date = info
return ( " PostNL \x02 %s \x0F est actuellement "
" \x02 %s \x0F vers le pays \x02 %s \x0F (Mis à jour le \x02 %s \x0F "
" ). " % ( tracknum , post_status , post_destination , post_date ) )
2017-08-10 04:48:48 +00:00
def handle_usps ( tracknum ) :
info = get_usps_info ( tracknum )
if info :
notif , last_date , last_status , last_location = info
2017-09-28 00:30:14 +00:00
return ( " USPS \x02 {tracknum} \x0F : {last_status} in \x02 {last_location} \x0F as of {last_date} : {notif} " . format ( tracknum = tracknum , notif = notif , last_date = last_date , last_status = last_status . lower ( ) , last_location = last_location ) )
2017-08-10 04:48:48 +00:00
2015-10-27 21:19:12 +00:00
def handle_colissimo ( tracknum ) :
info = get_colissimo_info ( tracknum )
2015-09-17 23:23:42 +00:00
if info :
date , libelle , site = info
2015-10-28 19:55:02 +00:00
return ( " Colissimo: \x02 %s \x0F : \x02 %s \x0F Dernière mise à jour le "
" \x02 %s \x0F au site \x02 %s \x0F . "
% ( tracknum , libelle , date , site ) )
2015-09-17 23:23:42 +00:00
2015-10-27 21:19:12 +00:00
def handle_chronopost ( tracknum ) :
info = get_chronopost_info ( tracknum )
2015-09-17 23:23:42 +00:00
if info :
2015-10-27 21:19:12 +00:00
date , libelle = info
2015-10-28 19:55:02 +00:00
return ( " Colis Chronopost: \x02 %s \x0F : \x02 %s \x0F . Dernière mise à "
" jour \x02 %s \x0F . " % ( tracknum , libelle , date ) )
2015-09-17 23:23:42 +00:00
2015-10-27 21:19:12 +00:00
def handle_coliprive ( tracknum ) :
info = get_colisprive_info ( tracknum )
2015-09-15 22:23:42 +00:00
if info :
2015-10-27 21:19:12 +00:00
return ( " Colis Privé: \x02 %s \x0F : \x02 %s \x0F . " % ( tracknum , info ) )
2015-09-15 22:23:42 +00:00
2015-10-31 16:40:23 +00:00
2017-07-08 12:38:24 +00:00
def handle_fedex ( tracknum ) :
info = get_fedex_info ( tracknum )
if info :
if info [ " displayActDeliveryDateTime " ] != " " :
return ( " {trackingCarrierDesc} : \x02 {statusWithDetails} \x0F : in \x02 {statusLocationCity} , {statusLocationCntryCD} \x0F , delivered on: {displayActDeliveryDateTime} . " . format ( * * info ) )
elif info [ " statusLocationCity " ] != " " :
return ( " {trackingCarrierDesc} : \x02 {statusWithDetails} \x0F : estimated delivery: {displayEstDeliveryDateTime} . " . format ( * * info ) )
else :
return ( " {trackingCarrierDesc} : \x02 {statusWithDetails} \x0F : in \x02 {statusLocationCity} , {statusLocationCntryCD} \x0F , estimated delivery: {displayEstDeliveryDateTime} . " . format ( * * info ) )
2017-08-09 22:55:13 +00:00
def handle_dhl ( tracknum ) :
info = get_dhl_info ( tracknum )
if info :
return " DHL {label} {id} : \x02 {description} \x0F " . format ( * * info )
2015-10-27 21:19:12 +00:00
TRACKING_HANDLERS = {
' laposte ' : handle_laposte ,
2017-07-02 17:08:01 +00:00
' postnl ' : handle_postnl ,
2015-10-27 21:19:12 +00:00
' colissimo ' : handle_colissimo ,
' chronopost ' : handle_chronopost ,
2015-10-29 01:10:46 +00:00
' coliprive ' : handle_coliprive ,
2015-10-31 16:40:23 +00:00
' tnt ' : handle_tnt ,
2017-07-08 12:38:24 +00:00
' fedex ' : handle_fedex ,
2017-08-09 22:55:13 +00:00
' dhl ' : handle_dhl ,
2017-08-10 04:48:48 +00:00
' usps ' : handle_usps ,
2015-10-27 21:19:12 +00:00
}
2015-09-15 22:23:42 +00:00
2015-10-28 19:55:02 +00:00
2015-10-27 21:19:12 +00:00
# HOOKS ##############################################################
2015-07-13 21:20:10 +00:00
2015-11-02 19:19:12 +00:00
@hook.command ( " track " ,
2015-10-31 16:40:23 +00:00
help = " Track postage delivery " ,
help_usage = {
" TRACKING_ID [...] " : " Track the specified postage IDs on various tracking services. "
} ,
keywords = {
" tracker=TRK " : " Precise the tracker (default: all) among: " + ' , ' . join ( TRACKING_HANDLERS )
} )
2015-10-27 21:19:12 +00:00
def get_tracking_info ( msg ) :
2015-07-13 21:20:10 +00:00
if not len ( msg . args ) :
2015-10-30 20:57:45 +00:00
raise IMException ( " Renseignez un identifiant d ' envoi. " )
2015-10-27 21:19:12 +00:00
res = Response ( channel = msg . channel , count = " ( %d suivis supplémentaires) " )
if ' tracker ' in msg . kwargs :
if msg . kwargs [ ' tracker ' ] in TRACKING_HANDLERS :
trackers = {
msg . kwargs [ ' tracker ' ] : TRACKING_HANDLERS [ msg . kwargs [ ' tracker ' ] ]
}
else :
2015-10-30 20:57:45 +00:00
raise IMException ( " No tracker named \x02 {tracker} \x0F , please use "
2015-10-28 19:55:02 +00:00
" one of the following: \x02 {trackers} \x0F "
. format ( tracker = msg . kwargs [ ' tracker ' ] ,
trackers = ' , '
. join ( TRACKING_HANDLERS . keys ( ) ) ) )
2015-10-27 21:19:12 +00:00
else :
trackers = TRACKING_HANDLERS
for tracknum in msg . args :
2015-10-28 19:55:02 +00:00
for name , tracker in trackers . items ( ) :
2015-10-27 21:19:12 +00:00
ret = tracker ( tracknum )
if ret :
res . append_message ( ret )
break
if not ret :
2015-10-28 19:55:02 +00:00
res . append_message ( " L ' identifiant \x02 {id} \x0F semble incorrect, "
" merci de vérifier son exactitude. "
. format ( id = tracknum ) )
2015-10-27 21:19:12 +00:00
return res