Modem 56K en filtre d'appels telephone fixe...

Forum dédié aux problématiques concernant les scripts pour DomoticZ.
Entourez votre code et les logs avec les balises nommées code grâce au bouton <\>.
lost
Messages : 1511
Inscription : 12 nov. 2016, 11:01

Re: Modem 56K en filtre d'appels telephone fixe...

Message par lost »

adv a écrit : 11 mai 2022, 21:22 Je connais rien en python mais un développeur un peu calé pourrait tout à fait développer un autre plugin, afin d'envoyer le numéro de l'appelant sur un site comme...
Pour ma part, j'ai utilisé doisjerepondre (en °1) qui fonctionne comme tellows via une page web mais sans API (=> nécessaire d'analyser la page de réponse pour en extraire la classification) qui vient désormais en second (même si dans le code ci dessous, c'est en 3, le 2 collant un captcha sur demandes répétées je l'avais supprimé): Bref, j'ai un code fonctionnel pour ces deux là, à adapter à callattendant...

Ayant utilisé initialement uniquement doisjerepondre qui donne une classification texte à 3 niveaux j'avais transformé la notation tellows dans ce mode car tout mon code était fait derrière pour traiter les "unknown"/"neutral"/"negative" de doisjerepondre. J'ai ajouté un perso, "not_digit" pour ceux qui bricolent les caller ID avec des caractères sortant de 0-9 par exemple (mon modem donne "P" pour un appel privé, sans envoi de caller ID, qui se retrouvera dans ce cas également)

Extraits de code python (sur les 1300 lignes de mon truc perso, sans compter la partie email/récupération d'infos Domoticz, qui m'envoie les numéros qui m'appellent par mail si je suis absent, s'ils sont des appels légitimes/non filtrés, c'est dans des modules python séparés) ci dessous pour la partie analyse de page réponse automatisée (via le module python beautifulsoup), avec les imports spécifiques (d'autres sans doute nécessaires, genre logger, mais non spécifiques) ; Le code concernant numeroinconnu.fr est là mais commenté, inutilsable cause captcha...

Code : Tout sélectionner

#
# IMPORTS specifiques:
#
from urllib2 import urlopen as urlopen2, Request
from bs4 import BeautifulSoup

# URLs check Caller ID:
CHECK_URL = 'http://www.doisjerepondre.fr/numero-de-telephone/'
#CHECK_URL2 = 'https://www.numeroinconnu.fr/numero/'
CHECK_URL3 = 'https://www.tellows.fr/num/'

#
# Fonctions CHECK CID (2 inactive):
#
def check_cid(caller_id, logger):
    # Input: Caller ID as a string like '0367079816' (ranked 'neutral') or '0367077296' ('negative')...
    #
    # May return hereunder "score xxx" substring from http response:
    # [<div class="scoreContainer">\n<div class="score unknown"></div>\n</div>]
    # [<div class="scoreContainer">\n<div class="score neutral"></div>\n</div>]
    # [<div class="scoreContainer">\n<div class="score negative"></div>\n</div>]

    # Immediately default to 'not_digit' pseudo-score if CID string
    # is not digit only (private call with no filled CID?)
    if not caller_id.isdigit():
        return 'not_digit'

    # Otherwise defaults to 'unknown'...
    ret = 'unknown'

    try:
        #check_page = urlopen1(CHECK_URL+caller_id)
        r = Request(CHECK_URL+caller_id, headers={'User-Agent': 'Mozilla/5.0'})
        check_page = urlopen2(r).read()
    except: # pylint: disable=bare-except
        logger.log(logging.INFO, "Connection failed : "+CHECK_URL)
        return ret

    check_soup = BeautifulSoup(check_page, "lxml")
    #print check_soup # DEBUG (very verbose...).
    try:
        score_raw = check_soup.find("div", attrs={'class':u'scoreContainer'})
        logger.log(logging.DEBUG,  score_raw)
        score = score_raw.div.get('class')
        if score[0] == 'score':
            ret = score[1]
        else:
            logger.log(logging.INFO, "Can't decode score result from "+CHECK_URL+caller_id)
        logger.log(logging.INFO, "%s scored %s", caller_id , ret)
    except: # pylint: disable=bare-except
        logger.log(logging.INFO, "CID check exception (page structure) for "+CHECK_URL+caller_id)

    return ret

"""
def check_cid2(caller_id, logger):
    # Input: Caller ID as a string like '0367079816' or '0367077296'...
    #
    # Should return "danger level" progress-bar value percentage from http response (here 69%):
    # <div id="progress-bar-wrapper"><div class="progress-bar-rank4" id="progress-bar-inner">69</div><div id="progress-bar-over"></div></div>

    # Immediately default to 'not_digit' pseudo-score if CID string
    # is not digit only (private call with no filled CID?)
    if not caller_id.isdigit():
        return 'not_digit'

    # Otherwise defaults to 'unknown'...
    ret = 'unknown'

    try:
        r = Request(CHECK_URL2+caller_id, headers={'User-Agent': 'Mozilla/5.0'})
        check_page = urlopen2(r).read()
    except: # pylint: disable=bare-except
        logger.log(logging.INFO, "Connection failed : "+CHECK_URL2)
        return ret

    check_soup = BeautifulSoup(check_page, "lxml")
    #print check_soup # DEBUG (very verbose...).
    try:
        scoreVal_raw = check_soup.find("div", attrs={'id':u'progress-bar-wrapper'})
        logger.log(logging.DEBUG,  scoreVal_raw)
        scoreVal = int(scoreVal_raw.contents[0].text.split()[0])
        if 0 <= scoreVal <= 100:
            if scoreVal < 35:
                ret = 'unknown'
            elif 35 <= scoreVal <= 65:
                ret = 'neutral'
            else:
                ret = 'negative'
        logger.log(logging.INFO, "%s scored %d:%s", caller_id , scoreVal, ret)

    except: # pylint: disable=bare-except
        logger.log(logging.INFO, "CID check exception (page structure) for "+CHECK_URL2+caller_id)
    return ret
"""

def check_cid3(caller_id, logger):
    # Input: Caller ID as a string like '0367079816' (ranked 'neutral') or '0367077296' ('negative')...
    #
    # May return hereunder "score" value from http response:
    # <div class="scorepic">
    #    <a style="line-height:0em;" href="https://blog.tellows.fr/tellows_score/" target="_blank">
    #        <img width="100" height="108" style="width: 100px;" class="scoreimage" src="/images/score/s8.jpg" alt="tellows Note pour  0516440080 : Score 8" />
    #    </a>
    # </div>

    # Immediately default to 'not_digit' pseudo-score if CID string
    # is not digit only (private call with no filled CID?)
    if not caller_id.isdigit():
        return 'not_digit'

    # Otherwise defaults to 'unknown'...
    ret = 'unknown'

    try:
        r = Request(CHECK_URL3+caller_id, headers={'User-Agent': 'Mozilla/5.0'})
        check_page = urlopen2(r).read()
    except: # pylint: disable=bare-except
        logger.log(logging.INFO, "Connection failed : "+CHECK_URL3)
        return ret

    check_soup = BeautifulSoup(check_page, "lxml")
    #print check_soup # DEBUG (very verbose...).
    try:
        scoreVal_raw = check_soup.find('img', attrs={'class':u'scoreimage', 'alt':True})['alt'].split()
        scoreVal = int(scoreVal_raw[-1])
        logger.log(logging.DEBUG, 'Score figure (1/9): %d', scoreVal)
        if 1 <= scoreVal <= 9:
            if scoreVal < 4:
                ret = 'unknown'
            elif 4 <= scoreVal <= 6:
                ret = 'neutral'
            else:
                ret = 'negative'
        logger.log(logging.INFO, "%s scored %d:%s", caller_id , scoreVal, ret)
    except: # pylint: disable=bare-except
        logger.log(logging.INFO, "CID check exception (page structure) for "+CHECK_URL3+caller_id)

    return ret

#
# USAGE : Verif en cascade, extrait de la fonction de filtragevenant en étape finale
#              après les blacklist locales (ID opérateur ou préfixes numéro isolés),
#             => Si doisjrepondre réponds "unknown", on utilise tellows...
#

(...)
        score = check_cid(caller_id, logger)
        # Double check if unknown for 1st online checker
        if score == 'unknown':
            score = check_cid3(caller_id, logger)
        if (score == 'negative') or (score == 'not_digit'):
            filterThisCall = True
            updateCfgBlackList(call_record, logger)

    # Filter call if nmbr blacklisted or scored negative
    if filterThisCall:
        logger.log(logging.INFO, "CID : " + caller_id + " (score = " + score + ") => Filter call !")
        modem_hold_on_off(logger)
    else:
        logger.log(logging.DEBUG, "CID : " + caller_id + " ; score = " + score)
(...)
Bien entendu, si les sites consultés changent un jour la structure de la page d'une manière qui fait coincer l'extraction automatisée de la partie résultat, un message "Can't decode score result..." ou "CID check exception (page structure)..." sera dans le syslog Linux et il faudra alors mettre à jour. Cela se fait en faisant une requête via son navigateur préféré et lui faisant afficher le code source de la page retournée, pour identifier ou se trouve la classification du numéro et y adapter la recherche/extraction automatique.

C'est pas directement réutilisable et tout n'y est pas: L'appel a modem_hold_on_off() est par exemple laissé: C'est l'endroit ou le décroché/raccroché est fait afin de filtrer l'appel. Cela peut aider à localiser ou greffer tout ceci dans callattendant...
lost
Messages : 1511
Inscription : 12 nov. 2016, 11:01

Re: Modem 56K en filtre d'appels telephone fixe...

Message par lost »

Autre petite astuce, examiner l'évolution des listes d'allocation ARCEP est parfois instructif. AInsi, sur la MAJ d'avril, un opérateur se distingue en apparaissant avoir pris de nombreuses plages d'allocation régionales, ce qui attire l'attention:

Code : Tout sélectionner

$ diff providerPrefixes.csv providerPrefixes.csv.old | grep BJTP
< "018970","BJTP"
< "021934","BJTP"
< "021935","BJTP"
< "021936","BJTP"
< "021937","BJTP"
< "022000","BJTP"
< "022001","BJTP"
< "022002","BJTP"
< "022003","BJTP"
< "025853","BJTP"
< "025938","BJTP"
< "025939","BJTP"
< "025940","BJTP"
< "025941","BJTP"
< "027955","BJTP"
< "027956","BJTP"
< "033966","BJTP"
< "035370","BJTP"
< "035371","BJTP"
< "035680","BJTP"
< "035681","BJTP"
< "037588","BJTP"
< "037589","BJTP"
< "037611","BJTP"
< "037630","BJTP"
< "037962","BJTP"
< "037963","BJTP"
< "037964","BJTP"
< "039218","BJTP"
< "039219","BJTP"
< "041202","BJTP"
< "041227","BJTP"
< "042096","BJTP"
< "042324","BJTP"
< "042343","BJTP"
< "044408","BJTP"
< "044936","BJTP"
< "044937","BJTP"
< "044938","BJTP"
< "044939","BJTP"
< "045137","BJTP"
< "045138","BJTP"
< "045139","BJTP"
< "045140","BJTP"
< "048551","BJTP"
< "048560","BJTP"
< "048561","BJTP"
< "051825","BJTP"
< "052549","BJTP"
< "052550","BJTP"
< "052551","BJTP"
< "053610","BJTP"
< "053700","BJTP"
< "053701","BJTP"
< "054824","BJTP"
< "054825","BJTP"
< "054826","BJTP"
< "054827","BJTP"
> "089975","BJTP"
BJTP apparait être "BJT Partners" ici (fichier MAJOPE ; MAJNUM étant les plages d'allocation):
https://extranet.arcep.fr/portail/Commu ... ation.aspx

Une petite recherche vaut mieux qu'un long discours:
https://www.google.fr/search?q=bjt+partner

Les avis google sont assez unanimes! Je pense que celui là, je peux le coller dans mes blacklist opérateurs direct, sans même attendre le 1er appel...

Pour l'instant, les opérateurs totalement blacklistés chez moi sont:

Code : Tout sélectionner

Providers filter list : ['UNALLOCATED', 'IPDI', 'LGC', 'THEC', 'YOOT', 'ALPL', 'LEGO', 'DVEN', 'VOXB', 'DGLP', 'UBIC', 'TELN', 'COLT', 'ALNI', 'VAST', 'SMAA', 'COCR', 'PHGS', 'OXIL']
Attention, des fois des petits malins changent de nom d'un mois à l'autre, également!
Répondre