Skip to content

Commit

Permalink
Merge pull request #115 from emxsys/phone_mask
Browse files Browse the repository at this point in the history
Add support for other countries
  • Loading branch information
emxsys authored Nov 5, 2020
2 parents 4efcec9 + d7898c2 commit 2202743
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 35 deletions.
44 changes: 44 additions & 0 deletions callattendant/app.cfg.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,42 @@ TESTING = False
# This should not be changed/overrriden except during development/testing
#DATABASE = "callattendant.db"

# PHONE_DISPLAY_SEPARATOR: Specify the character used to format phone numbers, e.g, a space, hyphen or period,
PHONE_DISPLAY_SEPARATOR = "-"

# PHONE_DISPLAY_FORMAT: Define the formatting of phone numbers in the various lists. You must use the
# separator character defined by PHONE_DISPLAY_SEPARATOR above in the format string.
#
# The phone display format handles variable length phone numbers. Excess digits not included
# in the format string are prepended to the number with a separator.
# For example, the AUS number 006173XXXYYYY would be formatted as follows:
# General format: 006173-XXX-YYYY
# AU format: 00-61-73-XXX-YYYY
# US format: 006-173-XXX-YYYY
# UK format: 00-6173-XXX-YYYY
# FR format: 0061-73X-XX-YY-YY
#
# Example: General
# PHONE_DISPLAY_FORMAT = "###-####"
#
# Example: US
# PHONE_DISPLAY_FORMAT = "###-###-####"
#
# Example: UK
# PHONE_DISPLAY_FORMAT = "####-###-####"
#
# Example: FR
# PHONE_DISPLAY_FORMAT = "###-##-##-##"
#
# Example: AU
# PHONE_DISPLAY_FORMAT = "##-##-###-####"
#
# Example: Raw - no formatting
# PHONE_DISPLAY_FORMAT = ""
#
PHONE_DISPLAY_FORMAT = "###-###-####"


# SCREENING_MODE: A tuple containing: "whitelist" and/or "blacklist", or empty
SCREENING_MODE = ("whitelist", "blacklist")

Expand All @@ -38,6 +74,14 @@ BLOCK_NAME_PATTERNS = {"V[0-9]{15}": "Telemarketer Caller ID", }
# Example: {"P": "Private number", "O": "Out of area",}
BLOCK_NUMBER_PATTERNS = {}

# BLOCK_SERVICE: The name of the online service used to lookup robocallers and spam numbers.
# Currently, only NOMOROBO is supported and it is for the USA. Areas outside the USA should set
# to blank. When the online service is blank (disabled), only the blacklist and blocked
# name/number patterns are used to block numbers.
#
# Example: "NOMOROBO" (USA) or "" (disabled).
BLOCK_SERVICE = "NOMOROBO"


# BLOCKED_ACTIONS: A tuple containing a combination of the following actions:
# "greeting", "record_message", "voice_mail".
Expand Down
5 changes: 5 additions & 0 deletions callattendant/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import sys
import queue
import sqlite3
import time
from pprint import pprint
from shutil import copyfile

Expand Down Expand Up @@ -115,6 +116,10 @@ def process_calls(self):
# Instruct the modem to start feeding calls into the caller queue
self.modem.handle_calls(self.handle_caller)

# If testing, allow queue to be filled before processing for clean, readable logs
if self.config["TESTING"]:
time.sleep(1)

# Process incoming calls
while 1:
try:
Expand Down
4 changes: 4 additions & 0 deletions callattendant/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@
"DATABASE": "callattendant.db",
"SCREENING_MODE": ("whitelist", "blacklist"),

"PHONE_DISPLAY_SEPARATOR": "-",
"PHONE_DISPLAY_FORMAT": "###-###-####",

"BLOCK_ENABLED": True,
"BLOCK_SERVICE": "NOMOROBO",
"BLOCK_NAME_PATTERNS": {"V[0-9]{15}": "Telemarketer Caller ID", },
"BLOCK_NUMBER_PATTERNS": {},

Expand Down
17 changes: 9 additions & 8 deletions callattendant/screening/callscreener.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,15 @@ def is_blacklisted(self, callerid):
reason = block["number_patterns"][key]
print(reason)
return True, reason
print(">> Checking nomorobo...")
result = self._nomorobo.lookup_number(number)
if result["spam"]:
reason = "{} with score {}".format(result["reason"], result["score"])
if self.config["DEBUG"]:
print(">>> {}".format(reason))
self.blacklist_caller(callerid, reason)
return True, reason
if block["service"].upper() == "NOMOROBO":
print(">> Checking nomorobo...")
result = self._nomorobo.lookup_number(number)
if result["spam"]:
reason = "{} with score {}".format(result["reason"], result["score"])
if self.config["DEBUG"]:
print(">>> {}".format(reason))
self.blacklist_caller(callerid, reason)
return True, reason
print("Caller has been screened")
return False, "Not found"
finally:
Expand Down
5 changes: 2 additions & 3 deletions callattendant/userinterface/templates/callers_blocked.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,8 @@ <h5 class="modal-title" id="addModalLabel">Add Blocked Number</h5>
<form>
<div class="modal-body">
<div class="form-group">
<label for="add-phone" class="col-form-label">Phone: ...-...-....</label>
<input name="phone" type="tel" class="form-control" id="add-phone" required="required"
pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" >
<label for="add-phone" class="col-form-label">Phone: {{ phone_no_format }}</label>
<input name="phone" type="tel" class="form-control" id="add-phone" required="required" >
</div>
<div class="form-group">
<label for="add-name" class="col-form-label">Name:</label>
Expand Down
5 changes: 2 additions & 3 deletions callattendant/userinterface/templates/callers_permitted.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,8 @@ <h5 class="modal-title" id="addModalLabel">Add Permitted Number</h5>
<form>
<div class="modal-body">
<div class="form-group">
<label for="add-phone" class="col-form-label">Phone: ...-...-....</label>
<input name="phone" type="tel" class="form-control" id="add-phone" required="required"
pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" >
<label for="add-phone" class="col-form-label">Phone: {{ phone_no_format }}</label>
<input name="phone" type="tel" class="form-control" id="add-phone" required="required" >
</div>
<div class="form-group">
<label for="add-name" class="col-form-label">Name:</label>
Expand Down
88 changes: 67 additions & 21 deletions callattendant/userinterface/webapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,7 @@ def calls():
search_criteria = ""
if search_text:
if search_type == "phone":
num_list = re.findall('[0-9]+', search_text)
number = "".join(num_list) # override GET arg if we're searching
number = transform_number(search_text) # override GET arg if we're searching
search_criteria = "WHERE Number='{}'".format(number)
else:
search_criteria = "WHERE Caller LIKE '%{}%'".format(search_text)
Expand Down Expand Up @@ -320,7 +319,7 @@ def calls():
calls = []
for row in result_set:
number = row[2]
phone_no = '{}-{}-{}'.format(number[0:3], number[3:6], number[6:])
phone_no = format_phone_no(number)
# Flask pages use the static folder to get resources.
# In the static folder we have created a soft-link to the
# data/messsages folder containing the actual messages.
Expand Down Expand Up @@ -403,7 +402,7 @@ def calls_view(call_no):
caller = {}
if len(row) > 0:
number = row[2]
phone_no = '{}-{}-{}'.format(number[0:3], number[3:6], number[6:])
phone_no = format_phone_no(number)
# Flask pages use the static folder to get resources.
# In the static folder we have created a soft-link to the
# data/messsages folder containing the actual messages.
Expand Down Expand Up @@ -448,7 +447,7 @@ def callers_manage(call_no):

# Post changes to the blacklist or whitelist table before rendering
if request.method == 'POST':
number = request.form['phone_no'].replace('-', '')
number = transform_number(request.form['phone_no'])
if request.form['action'] == 'add-permit':
caller = {}
caller['NMBR'] = number
Expand Down Expand Up @@ -503,7 +502,7 @@ def callers_manage(call_no):
number = record[2]
caller.update(dict(
call_no=record[0],
phone_no='{}-{}-{}'.format(number[0:3], number[3:6], number[6:]),
phone_no=format_phone_no(number),
name=record[1],
whitelisted=record[3],
blacklisted=record[4],
Expand Down Expand Up @@ -544,7 +543,7 @@ def callers_blocked():
records = []
for record in result_set:
number = record[0]
phone_no = '{}-{}-{}'.format(number[0:3], number[3:6], number[6:])
phone_no = format_phone_no(number)
records.append(dict(
Phone_Number=phone_no,
Name=record[1],
Expand All @@ -564,6 +563,7 @@ def callers_blocked():
return render_template(
'callers_blocked.html',
active_nav_item='blocked',
phone_no_format=current_app.config.get("MASTER_CONFIG").get("PHONE_DISPLAY_FORMAT"),
blacklist=records,
page=page,
per_page=per_page,
Expand All @@ -577,8 +577,7 @@ def callers_blocked_add():
Add a new blacklist entry
"""
caller = {}
# TODO: Strip all none digits from phone via regex
number = request.form["phone"].replace('-', '')
number = transform_number(request.form["phone"])
caller['NMBR'] = number
caller['NAME'] = request.form["name"]
print("Adding " + number + " to blacklist")
Expand All @@ -596,7 +595,7 @@ def callers_blocked_update(phone_no):
"""
Update the blacklist entry associated with the phone number.
"""
number = phone_no.replace('-', '')
number = transform_number(phone_no)
print("Updating " + number + " in blacklist")
blacklist = Blacklist(get_db(), current_app.config)
blacklist.update_number(number, request.form['name'], request.form['reason'])
Expand All @@ -609,7 +608,7 @@ def callers_blocked_delete(phone_no):
"""
Delete the blacklist entry associated with the phone number.
"""
number = phone_no.replace('-', '')
number = transform_number(phone_no)

print("Removing " + number + " from blacklist")
blacklist = Blacklist(get_db(), current_app.config)
Expand All @@ -636,7 +635,7 @@ def callers_permitted():
records = []
for record in result_set:
number = record[0]
phone_no = '{}-{}-{}'.format(number[0:3], number[3:6], number[6:])
phone_no = format_phone_no(number)
records.append(dict(
Phone_Number=phone_no,
Name=record[1],
Expand All @@ -655,6 +654,7 @@ def callers_permitted():
return render_template(
'callers_permitted.html',
active_nav_item='permitted',
phone_no_format=current_app.config.get("MASTER_CONFIG").get("PHONE_DISPLAY_FORMAT"),
whitelist=records,
total_calls=total,
page=page,
Expand All @@ -669,8 +669,7 @@ def callers_permitted_add():
Add a new whitelist entry
"""
caller = {}
# TODO: Strip all none digits from phone via regex
number = request.form['phone'].replace('-', '')
number = transform_number(request.form['phone'])
caller['NMBR'] = number
caller['NAME'] = request.form['name']
print("Adding " + number + " to whitelist")
Expand All @@ -688,7 +687,8 @@ def callers_permitted_update(phone_no):
"""
Update the whitelist entry associated with the phone number.
"""
number = phone_no.replace('-', '')
number = transform_number(phone_no)

print("Updating " + number + " in whitelist")
whitelist = Whitelist(get_db(), current_app.config)
whitelist.update_number(number, request.form['name'], request.form['reason'])
Expand All @@ -701,7 +701,7 @@ def callers_permitted_delete(phone_no):
"""
Delete the whitelist entry associated with the phone number.
"""
number = phone_no.replace('-', '')
number = transform_number(phone_no)

print("Removing " + number + " from whitelist")
whitelist = Whitelist(get_db(), current_app.config)
Expand Down Expand Up @@ -763,7 +763,7 @@ def messages():
msg_no=row[0],
call_no=row[1],
name=row[2],
phone_no='{}-{}-{}'.format(number[0:3], number[3:6], number[6:]),
phone_no=format_phone_no(number),
wav_file=filepath,
msg_played=row[5],
date=date_time.strftime('%d-%b-%y'),
Expand Down Expand Up @@ -865,11 +865,53 @@ def settings():


def format_phone_no(number):
return'{}-{}-{}'.format(number[0:3], number[3:6], number[6:])
'''
Returns a formatted the phone number based on the PHONE_DISPLAY_FORMAT configuration setting.
'''
config = current_app.config.get("MASTER_CONFIG")
template = config.get("PHONE_DISPLAY_FORMAT")
separator = config.get("PHONE_DISPLAY_SEPARATOR")
if separator == "" or template == "":
return number

# Get the template and split into reverse ordered parts for processing
tmpl_parts = template.split(separator)
tmpl_parts.reverse()

# Piece together the phone no from right to left to handle variable len numbers
number_len = len(number)
end = number_len
total_digits = 0
phone_parts = []
for tmpl in tmpl_parts:
# Assemble parts from right to left
start = max(0, end - len(tmpl))
digits = number[start: end]
phone_parts.insert(0, digits)
# Prepare for next part
end = start
total_digits += len(digits)
# if number is shorter than template then exit loop
if start == 0:
break
# If number is longer then template, then capture remaining digits
if total_digits < number_len:
# Prepend remaining digits to parts
phone_parts.insert(0, number[0: number_len - total_digits])
# Return the formatted number
return separator.join(phone_parts)

def transform_number(phone_no):
'''
Returns the phone no stripped of all non-alphanumeric characters and makes uppercase.
'''
return "".join(filter(str.isalnum, phone_no)).upper()


def get_db():
'''Get a connection to the database'''
'''
Get a connection to the database
'''
# Flask template for database connections
if 'db' not in g:
master_config = current_app.config.get("MASTER_CONFIG")
Expand All @@ -883,7 +925,9 @@ def get_db():


def close_db(e=None):
'''Clost the connection to the database'''
'''
Clost the connection to the database
'''
# Flask template for database connections
db = g.pop('db', None)

Expand All @@ -892,7 +936,9 @@ def close_db(e=None):


def get_row_count(table_name):
'''Returns the row count for the given table'''
'''
Returns the row count for the given table
'''
# Using the current request's db connection
sql = 'select count(*) from {}'.format(table_name)
g.cur.execute(sql)
Expand Down
2 changes: 2 additions & 0 deletions tests/test_webapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ def myapp():

master_config = {
"DB_FILE": db_path,
"PHONE_DISPLAY_FORMAT": "###-###-####",
"PHONE_DISPLAY_SEPARATOR": "-",
}

app.config['MASTER_CONFIG'] = master_config
Expand Down

0 comments on commit 2202743

Please sign in to comment.