12

I am attempting to filter every notification through espeak. However, I can't seem to find a way to get the notification body from a python script, or even what signal_name to listen to.

bus.add_signal_receiver(espeak,
                    dbus_interface="org.freedesktop.Notifications",
                    signal_name="??")

Trying to google for this only seems to yield results involving creating new notifications, so I am completely lost now.

Anyone can help me with this?

In short, what I want is to listen for incoming notifications using python, and obtaining the "body" attribute of the notification.

manmachine
  • 121
  • 1
  • 1
  • 3
  • 1
    It seems a notification doesn't produce a signal i.e., `dbus-monitor "type='signal',interface='org.freedesktop.Notifications'"` shows nothing but `dbus-monitor "interface='org.freedesktop.Notifications'"` shows notifications (type is 'method_call' not 'signal'). – jfs Sep 07 '12 at 21:34

4 Answers4

15

To keep this up to date: from dbus 1.5.something an extra parameter is required when adding a match string with bus.add_match_string_non_blocking to make sure we receive everything.

The resulting code would be the following:

import glib
import dbus
from dbus.mainloop.glib import DBusGMainLoop

def notifications(bus, message):
    print [arg for arg in message.get_args_list()]

DBusGMainLoop(set_as_default=True)

bus = dbus.SessionBus()
bus.add_match_string_non_blocking("eavesdrop=true, interface='org.freedesktop.Notifications', member='Notify'")
bus.add_message_filter(notifications)

mainloop = glib.MainLoop()
mainloop.run()
joost
  • 151
  • 1
  • 2
  • If I want to call another different dbus method inside notification filter, it doesn't work. All I get `unable to connect to session bus: Operation was cancelled`. We are passing `bus` to the filter. – Khurshid Alam Nov 16 '18 at 18:23
  • 5
    On my Python installation (Python 3, Ubuntu) I needed `from gi.repository import GLib as glib` to make this work. – Owen Jan 27 '20 at 03:04
  • 1
    Thanks for this! I ended up using this to do a (bad?) notification filter: https://gist.github.com/eacousineau/63651e498ddc31a7c1478f8638d0fd2e#file-scrub_notifications-py – eacousineau Nov 25 '20 at 22:17
  • Thanks, eacousineau! Your script works on Ubuntu 22.04 – Alek_A Jun 15 '22 at 10:21
7

By notifications you mean the "OSD bubbles" that some software sends, like changing volume, IM chat, etc? You want to create a python program to capture those?

Well, Ask Ubuntu is not a programmer's QA, and software development is a bit beyond the scope, but here is a little code I did do capture notification bubbles:

import glib
import dbus
from dbus.mainloop.glib import DBusGMainLoop

def notifications(bus, message):
    if message.get_member() == "Notify":
        print [arg for arg in message.get_args_list()]

DBusGMainLoop(set_as_default=True)

bus = dbus.SessionBus()
bus.add_match_string_non_blocking("interface='org.freedesktop.Notifications'")
bus.add_message_filter(notifications)

mainloop = glib.MainLoop()
mainloop.run()

Leave this running in a terminal, then open another terminal window and test it:

notify-send --icon=/usr/share/pixmaps/debian-logo.png "My Title" "Some text body"

And the program will output this:

[dbus.String(u'notify-send'), dbus.UInt32(0L), dbus.String(u'/usr/share/pixmaps/debian-logo.png'), dbus.String(u'My Title'), dbus.String(u'Some text body'),...

As you may have guessed, message.get_args_list()[0] is the sender, [2] for icon, [3] for summary and [4] for body text.

For the meaning of the other fields, check the official specification docs

MestreLion
  • 19,731
  • 12
  • 87
  • 108
  • Looks like it no longer works as of sometime on or before 16.04. Joost's answer below does fix it. – Catskul Jun 16 '16 at 15:37
3

I had trouble getting any of the other examples to actually work but I got there in the end. Here's a working example:

import glib
import dbus
from dbus.mainloop.glib import DBusGMainLoop

def print_notification(bus, message):
  keys = ["app_name", "replaces_id", "app_icon", "summary",
          "body", "actions", "hints", "expire_timeout"]
  args = message.get_args_list()
  if len(args) == 8:
    notification = dict([(keys[i], args[i]) for i in range(8)])
    print notification["summary"], notification["body"]

loop = DBusGMainLoop(set_as_default=True)
session_bus = dbus.SessionBus()
session_bus.add_match_string("type='method_call',interface='org.freedesktop.Notifications',member='Notify',eavesdrop=true")
session_bus.add_message_filter(print_notification)

glib.MainLoop().run()

If you want to see a more detailed working example I recommend looking at Notifications.py in the recent_notifications project.

kzar
  • 199
  • 6
0

The above example did not work at first, so I made some small changes and now it works perfectly.

import gi
from gi.repository import GLib
import dbus
from dbus.mainloop.glib import DBusGMainLoop

def print_notification(bus, message):
  keys = ["app_name", "replaces_id", "app_icon", "summary",
          "body", "actions", "hints", "expire_timeout"]
  args = message.get_args_list()
  if len(args) == 8:
    notification = dict([(keys[i], args[i]) for i in range(8)])
    print(notification["summary"], notification["body"])

loop = DBusGMainLoop(set_as_default=True)
session_bus = dbus.SessionBus()
session_bus.add_match_string("type='method_call',interface='org.freedesktop.Notifications',member='Notify',eavesdrop=true")
session_bus.add_message_filter(print_notification)

GLib.MainLoop().run()
Magnotec
  • 1
  • 1