From 0ea302b1f0dc367be78c4485d8b180eff982065a Mon Sep 17 00:00:00 2001
From: Bruce Schubert <bruce@emxsys.com>
Date: Wed, 4 Nov 2020 16:26:51 -0800
Subject: [PATCH 1/2] Added ability to permit callers based on permitted
 name/number regex patterns. - Added PERMIT_NAME_PATTERNS and
 PERMIT_NUMBER_PATTERNS configuration settings. - Re: #117

---
 callattendant/app.cfg.example           |  6 +++++
 callattendant/config.py                 |  3 +++
 callattendant/screening/callscreener.py | 25 ++++++++++++++++++-
 tests/test_callscreener.py              | 32 ++++++++++++++++++-------
 4 files changed, 57 insertions(+), 9 deletions(-)

diff --git a/callattendant/app.cfg.example b/callattendant/app.cfg.example
index baad0b4..9ac72b7 100644
--- a/callattendant/app.cfg.example
+++ b/callattendant/app.cfg.example
@@ -89,7 +89,13 @@ SCREENED_GREETING_FILE = "resources/general_greeting.wav"
 #   Example: 0 to act immediately, possibly before your local phone rings.
 SCREENED_RINGS_BEFORE_ANSWER = 0
 
+# PERMIT_NAME_PATTERNS: A regex expression dict applied to the CID names
+#   Example: {".*DOE": "Family maybe", "O": "Unknown caller", }
+PERMIT_NAME_PATTERNS = {}
 
+# PERMIT_NUMBER_PATTERNS: A regx expression dict applied to the CID numbers
+#   Example: {"01628": "My area", }
+PERMIT_NUMBER_PATTERNS = {}
 
 # PERMITTED_ACTIONS:  A tuple containing a combination of the following actions:
 #   "greeting", "record_message", "voice_mail". See BLOCKED_ACTIONS for more info.
diff --git a/callattendant/config.py b/callattendant/config.py
index 392efa5..9b34242 100644
--- a/callattendant/config.py
+++ b/callattendant/config.py
@@ -40,6 +40,9 @@
     "SCREENED_GREETING_FILE": "resources/general_greeting.wav",
     "SCREENED_RINGS_BEFORE_ANSWER": 0,
 
+    "PERMIT_NAME_PATTERNS": {},
+    "PERMT_NUMBER_PATTERNS": {},
+
     "PERMITTED_ACTIONS": (),
     "PERMITTED_GREETING_FILE": "resources/general_greeting.wav",
     "PERMITTED_RINGS_BEFORE_ANSWER": 4,
diff --git a/callattendant/screening/callscreener.py b/callattendant/screening/callscreener.py
index f81d444..2fb6048 100644
--- a/callattendant/screening/callscreener.py
+++ b/callattendant/screening/callscreener.py
@@ -37,7 +37,30 @@ class CallScreener(object):
 
     def is_whitelisted(self, callerid):
         '''Returns true if the number is on a whitelist'''
-        return self._whitelist.check_number(callerid['NMBR'])
+        number = callerid['NMBR']
+        name = callerid["NAME"]
+        permit = self.config.get_namespace("PERMIT_")
+        try:
+            is_whitelisted, reason = self._whitelist.check_number(callerid['NMBR'])
+            if is_whitelisted:
+                return True, reason
+            else:
+                print(">> Checking permitted patterns...")
+                for key in permit["name_patterns"].keys():
+                    match = re.search(key, name)
+                    if match:
+                        reason = permit["name_patterns"][key]
+                        print(reason)
+                        return True, reason
+                for key in permit["number_patterns"].keys():
+                    match = re.search(key, number)
+                    if match:
+                        reason = permit["number_patterns"][key]
+                        print(reason)
+                        return True, reason
+                return False, "Not found"
+        finally:
+            sys.stdout.flush()
 
     def is_blacklisted(self, callerid):
         '''Returns true if the number is on a blacklist'''
diff --git a/tests/test_callscreener.py b/tests/test_callscreener.py
index 5036020..d1b72c4 100644
--- a/tests/test_callscreener.py
+++ b/tests/test_callscreener.py
@@ -35,16 +35,19 @@
 
 
 # Create a blocked caller
-caller1 = {"NAME": "caller1", "NMBR": "1234567890", "DATE": "1012", "TIME": "0600"}
+caller1 = {"NAME": "CALLER1", "NMBR": "1234567890", "DATE": "1012", "TIME": "0600"}
 # Create a permitted caller
-caller2 = {"NAME": "caller2", "NMBR": "1111111111", "DATE": "1012", "TIME": "0600"}
-# Create a V123456789012345 Telemarketer caller
+caller2 = {"NAME": "CALLER2", "NMBR": "1111111111", "DATE": "1012", "TIME": "0600"}
+# Create a V123456789012345 Telemarketer caller (blocked name pattern match)
 caller3 = {"NAME": "V123456789012345", "NMBR": "80512345678", "DATE": "1012", "TIME": "0600"}
 # Create a robocaller
-caller4 = {"NAME": "caller4", "NMBR": "3105241189", "DATE": "1012", "TIME": "0600"}
-# Create a Private Number
-caller5 = {"NAME": "caller5", "NMBR": "P", "DATE": "1012", "TIME": "0600"}
-
+caller4 = {"NAME": "CALLER4", "NMBR": "3105241189", "DATE": "1012", "TIME": "0600"}
+# Create a Private Number (blocked number pattern match)
+caller5 = {"NAME": "CALLER5", "NMBR": "P", "DATE": "1012", "TIME": "0600"}
+# Create a John Doe name (permitted name pattern match)
+caller6 = {"NAME": "JOHN DOE", "NMBR": "0987654321", "DATE": "1012", "TIME": "0600"}
+# Create a unique number (permitted number pattern match)
+caller7 = {"NAME": "CALLER7", "NMBR": "09876543210", "DATE": "1012", "TIME": "0600"}
 
 @pytest.fixture(scope='module')
 def screener():
@@ -62,7 +65,12 @@ def screener():
     config['BLOCK_NUMBER_PATTERNS'] = {
         "P": "Private number",
     }
-
+    config['PERMIT_NAME_PATTERNS'] = {
+        ".*DOE": "Anyone",
+    }
+    config['PERMIT_NUMBER_PATTERNS'] = {
+        "987654": "Anyone",
+    }
     # Create the blacklist to be tested
     screener = CallScreener(db, config)
     # Add a record to the blacklist
@@ -106,3 +114,11 @@ def test_is_blacklisted_by_nomorobo(screener):
 def test_blocked_number_pattern(screener):
     is_blacklisted, reason = screener.is_blacklisted(caller5)
     assert is_blacklisted, "caller1 should be blocked by number pattern"
+
+def test_permitted_name_pattern(screener):
+    is_whitelisted, reason = screener.is_whitelisted(caller6)
+    assert is_whitelisted, "caller6 should be permiteed by name pattern"
+
+def test_permitted_number_pattern(screener):
+    is_whitelisted, reason = screener.is_whitelisted(caller7)
+    assert is_whitelisted, "caller7 should be permiteed by number pattern"

From 99d4dc23e4f402c8d934764cab4ac2bd41e1b909 Mon Sep 17 00:00:00 2001
From: Bruce Schubert <bruce@emxsys.com>
Date: Wed, 4 Nov 2020 16:39:21 -0800
Subject: [PATCH 2/2] Fixed typo in config.

---
 callattendant/config.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/callattendant/config.py b/callattendant/config.py
index 9b34242..772e119 100644
--- a/callattendant/config.py
+++ b/callattendant/config.py
@@ -41,7 +41,7 @@
     "SCREENED_RINGS_BEFORE_ANSWER": 0,
 
     "PERMIT_NAME_PATTERNS": {},
-    "PERMT_NUMBER_PATTERNS": {},
+    "PERMIT_NUMBER_PATTERNS": {},
 
     "PERMITTED_ACTIONS": (),
     "PERMITTED_GREETING_FILE": "resources/general_greeting.wav",