This is a bit of a necro, but since this is the only topic that shows up in web search related to this, I'll post the Python script I made for this based on the MM dbus API.
It requires installing pydbus, running as root (to be able to call `call.Hangup`), and is of course specific to ModemManager.
Note that one obvious downside of a script like this is that both the script and the actual calls program (eg gnome-calls) receive the incoming call notification at the same time and thus act on it in parallel. This means that it's likely that the calls program will ring, log the incoming call, etc before the script hangs up. Having a way to block the call without ringing, logging, etc would require inserting the logic between MM and the calls program, which requires modifying MM or (more likely) the calls program.
So there is still value in following up with gnome-calls etc to add a call-blocking feature.
---
Edit: This script has a hard-coded list of numbers for blocking. A nicer approach would be to block all numbers that aren't in your contacts list.
If you use gnome-contacts, you can modify the script talk to evolution-data-server via its D-Bus API. Note that this requires running in the D-Bus user session of the logged-in user.
Alternatively, eds stores contacts in a sqlite DB that is easy to query from Python, so you could consider doing that. Just be aware of the dangers of relying on internal details and of racing with eds to read the file as it writes to the file.
Code:
#!/usr/bin/python3
import gi
import pydbus
blocked_numbers = frozenset((
'+1123456789',
'+1987654321',
))
MM_CALL_DIRECTION_INCOMING = 1
MM_CALL_STATE_RINGING_IN = 3
call_added_subscriptions = {}
def try_modem_added(object_path, interfaces):
if 'org.freedesktop.ModemManager1.Modem.Voice' in interfaces:
print(f"added modem {object_path}", flush=True)
modem = bus.get('.ModemManager1', object_path)['org.freedesktop.ModemManager1.Modem.Voice']
subscription = modem.CallAdded.connect(call_added)
call_added_subscriptions[object_path] = subscription
def try_modem_removed(object_path, interfaces):
if 'org.freedesktop.ModemManager1.Modem.Voice' in interfaces:
print(f"removed modem {object_path}", flush=True)
subscription = call_added_subscriptions.pop(object_path, None)
if subscription is not None:
subscription.disconnect()
def call_added(object_path):
call = bus.get('.ModemManager1', object_path)['org.freedesktop.ModemManager1.Call']
if call.Direction == MM_CALL_DIRECTION_INCOMING and call.State == MM_CALL_STATE_RINGING_IN:
call_number = call.Number
print(f"added incoming call from {call_number}", flush=True)
if call_number in blocked_numbers:
print(f"{call_number} is in blocked numbers list")
call.Hangup()
print(f"hung up on {call_number}")
bus = pydbus.SystemBus()
manager = bus.get('.ModemManager1')['org.freedesktop.DBus.ObjectManager']
manager.InterfacesAdded.connect(try_modem_added)
manager.InterfacesRemoved.connect(try_modem_removed)
for object_path, interfaces in manager.GetManagedObjects().items():
try_modem_added(object_path, interfaces)
loop = gi.repository.GLib.MainLoop()
loop.run()
It requires installing pydbus, running as root (to be able to call `call.Hangup`), and is of course specific to ModemManager.
Note that one obvious downside of a script like this is that both the script and the actual calls program (eg gnome-calls) receive the incoming call notification at the same time and thus act on it in parallel. This means that it's likely that the calls program will ring, log the incoming call, etc before the script hangs up. Having a way to block the call without ringing, logging, etc would require inserting the logic between MM and the calls program, which requires modifying MM or (more likely) the calls program.
So there is still value in following up with gnome-calls etc to add a call-blocking feature.
---
Edit: This script has a hard-coded list of numbers for blocking. A nicer approach would be to block all numbers that aren't in your contacts list.
If you use gnome-contacts, you can modify the script talk to evolution-data-server via its D-Bus API. Note that this requires running in the D-Bus user session of the logged-in user.
Alternatively, eds stores contacts in a sqlite DB that is easy to query from Python, so you could consider doing that. Just be aware of the dangers of relying on internal details and of racing with eds to read the file as it writes to the file.