feat(voicemail): implement new voicemail notification callback and enhance call history retrieval with search and pagination support

This commit is contained in:
2026-01-01 21:08:17 -06:00
parent 5d3d5114a5
commit a6b01321a3
5 changed files with 84 additions and 5 deletions

View File

@@ -372,6 +372,7 @@ class ReticulumMeshChat:
self.voicemail_manager.get_name_for_identity_hash = (
self.get_name_for_identity_hash
)
self.voicemail_manager.on_new_voicemail_callback = self.on_new_voicemail_received
# init Ringtone Manager
self.ringtone_manager = RingtoneManager(
@@ -1335,8 +1336,39 @@ class ReticulumMeshChat:
value = value.lower()
return value in {"1", "true", "yes", "on"}
def on_new_voicemail_received(self, remote_hash, remote_name, duration):
AsyncUtils.run_async(
self.websocket_broadcast(
json.dumps(
{
"type": "new_voicemail",
"remote_identity_hash": remote_hash,
"remote_identity_name": remote_name,
"duration": duration,
"timestamp": time.time(),
},
),
),
)
# handle receiving a new audio call
def on_incoming_telephone_call(self, caller_identity: RNS.Identity):
caller_hash = caller_identity.hash.hex()
# Check if caller is blocked
if self.is_destination_blocked(caller_hash):
print(f"Rejecting incoming call from blocked source: {caller_hash}")
if self.telephone_manager.telephone:
self.telephone_manager.telephone.hangup()
return
# Check for Do Not Disturb
if self.config.do_not_disturb_enabled.get():
print(f"Rejecting incoming call due to Do Not Disturb: {caller_hash}")
if self.telephone_manager.telephone:
self.telephone_manager.telephone.hangup()
return
# Trigger voicemail handling
self.voicemail_manager.handle_incoming_call(caller_identity)
@@ -1403,6 +1435,21 @@ class ReticulumMeshChat:
timestamp=time.time(),
)
# Trigger missed call notification if it was an incoming call that ended while ringing
if is_incoming and status_code == 4:
AsyncUtils.run_async(
self.websocket_broadcast(
json.dumps(
{
"type": "telephone_missed_call",
"remote_identity_hash": remote_identity_hash,
"remote_identity_name": remote_identity_name,
"timestamp": time.time(),
},
),
),
)
AsyncUtils.run_async(
self.websocket_broadcast(
json.dumps(
@@ -3139,7 +3186,13 @@ class ReticulumMeshChat:
@routes.get("/api/v1/telephone/history")
async def telephone_history(request):
limit = int(request.query.get("limit", 10))
history = self.database.telephone.get_call_history(limit=limit)
offset = int(request.query.get("offset", 0))
search = request.query.get("search", None)
history = self.database.telephone.get_call_history(
search=search,
limit=limit,
offset=offset,
)
call_history = []
for row in history:

View File

@@ -135,6 +135,9 @@ class ConfigManager:
self.custom_ringtone_enabled = self.BoolConfig(self, "custom_ringtone_enabled", False)
self.ringtone_filename = self.StringConfig(self, "ringtone_filename", None)
# telephony config
self.do_not_disturb_enabled = self.BoolConfig(self, "do_not_disturb_enabled", False)
# map config
self.map_offline_enabled = self.BoolConfig(self, "map_offline_enabled", False)
self.map_offline_path = self.StringConfig(self, "map_offline_path", None)

View File

@@ -2,7 +2,7 @@ from .provider import DatabaseProvider
class DatabaseSchema:
LATEST_VERSION = 18
LATEST_VERSION = 19
def __init__(self, provider: DatabaseProvider):
self.provider = provider
@@ -551,6 +551,11 @@ class DatabaseSchema:
"CREATE INDEX IF NOT EXISTS idx_contacts_remote_identity_hash ON contacts(remote_identity_hash)",
)
if current_version < 19:
self.provider.execute(
"CREATE INDEX IF NOT EXISTS idx_call_history_remote_name ON call_history(remote_identity_name)",
)
# Update version in config
self.provider.execute(
"""

View File

@@ -40,10 +40,19 @@ class TelephoneDAO:
),
)
def get_call_history(self, limit=10):
def get_call_history(self, search=None, limit=10, offset=0):
if search:
return self.provider.fetchall(
"""
SELECT * FROM call_history
WHERE remote_identity_name LIKE ? OR remote_identity_hash LIKE ?
ORDER BY timestamp DESC LIMIT ? OFFSET ?
""",
(f"%{search}%", f"%{search}%", limit, offset),
)
return self.provider.fetchall(
"SELECT * FROM call_history ORDER BY timestamp DESC LIMIT ?",
(limit,),
"SELECT * FROM call_history ORDER BY timestamp DESC LIMIT ? OFFSET ?",
(limit, offset),
)
def clear_call_history(self):

View File

@@ -34,6 +34,8 @@ class VoicemailManager:
self.recording_remote_identity = None
self.recording_filename = None
self.on_new_voicemail_callback = None
# Paths to executables
self.espeak_path = self._find_espeak()
self.ffmpeg_path = self._find_ffmpeg()
@@ -377,6 +379,13 @@ class VoicemailManager:
f"Saved voicemail from {RNS.prettyhexrep(self.recording_remote_identity.hash)} ({duration}s)",
RNS.LOG_DEBUG,
)
if self.on_new_voicemail_callback:
self.on_new_voicemail_callback(
self.recording_remote_identity.hash.hex(),
remote_name,
duration,
)
else:
# Delete short/empty recording
filepath = os.path.join(self.recordings_dir, self.recording_filename)