Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for other countries #115

Merged
merged 6 commits into from
Nov 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -62,14 +62,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